【问题标题】:Merging jumping pairs合并跳跃对
【发布时间】:2019-05-30 22:21:33
【问题描述】:

如何递归地合并列表列表的元素的跳跃对?我需要有

'((a b c) (e d f) (g h i))

来自

'((a b) c (e d) f (g h) i)

我的尝试

(define (f lst)      
  (if (or (null? lst)            
          (null? (cdr lst))) 
      '()                                                          
      (cons (append (car lst) (list (cadr lst))) 
            (list (append (caddr lst) (cdddr lst))))))

如果我定义则有效

(define listi '((a b) c (d e) f))

我从中获得

((a b c) (d e f))

简单地做

(f listi)

但它不适用于更长的列表。我知道我需要递归,但我不知道在代码的最后一句中再次插入 f 的位置。

【问题讨论】:

    标签: list recursion racket


    【解决方案1】:

    您的算法失败的更简单情况:(f '((1 2) 3)) 应该导致 '((1 2 3)),但您的会导致错误。

    我们将首先定义一些术语:

    1. “元素”是常规元素,如1'a

    2. “普通列表”只是没有嵌套列表的“元素”列表。 例如,'(1 2 3) 是一个普通列表。 '((1 2) 3) 不是一个简单的列表。 “简单列表”是:

      • empty 列表
      • 一个“元素”和下一个“普通列表”的cons
    3. “跳跃对列表”是偶数长度的列表,其中奇数索引有一个“普通列表”,偶数索引有一个元素。例如,'((1) 2 (a) 4) 是“跳跃对列表”。 “跳跃对列表”是:

      • empty 列表
      • cons
        1. 一个“简单的列表”
        2. 一个“元素”和下一个“跳跃对列表”的cons

    我们已经完成了术语。在编写函数之前,让我们从一些例子开始:

    (f '())              equivalent to (f empty)
    
                         should output '()
                         equivalent to empty
    
    (f '((1 2) 3))       equivalent to (f (cons (cons 1 (cons 2 empty)) 
                                                (cons 3 
                                                      empty)))
    
                         should output '((1 2 3))
                         equivalent to (cons (cons 1 (cons 2 (cons 3 empty)))
                                             empty)
    
    (f '((1 2) 3 (4) a)) equivalent to (f (cons (cons 1 (cons 2 empty)) 
                                                (cons 3
                                                      (cons (cons 4 empty)
                                                            (cons 'a 
                                                                  empty)))))
    
                         should output '((1 2 3) (4 a))
                         equivalent to (cons (cons 1 (cons 2 (cons 3 empty)))
                                             (cons (cons 4 (cons 'a empty))
                                                   empty))
    

    所以,f 是一个函数,它使用“跳跃对列表”并返回“普通列表”列表。

    现在我们将编写函数f

    (define (f lst)
      ???)
    

    注意lst的类型是“跳对列表”,所以我们直接对其进行案例分析:

    (define (f lst)      
      (cond
        [(empty? lst) ???]               ;; the empty list case
    
        [else         ???                ;; the cons case has
                      (first lst)        ;; the "plain list",
                      (first (rest lst)) ;; the "element", and
                      (rest (rest lst))  ;; the next "list of jumping pairs"
                      ???]))             ;; that are available for us to use
    

    从例子:

    (f '())              equivalent to (f empty)
    
                         should output '()
                         equivalent to empty
    

    我们知道空的情况应该返回一个空列表,所以让我们相应地填补这个漏洞:

    (define (f lst)      
      (cond
        [(empty? lst) empty]             ;; the empty list case
    
        [else         ???                ;; the cons case has
                      (first lst)        ;; the "plain list",
                      (first (rest lst)) ;; the "element", and
                      (rest (rest lst))  ;; the next "list of jumping pairs"
                      ???]))             ;; that are available for us to use
    

    从例子:

    (f '((1 2) 3))       equivalent to    (f (cons (cons 1 (cons 2 empty)) 
                                                   (cons 3 
                                                         empty)))
    
                         should output '((1 2 3))
                         equivalent to (cons (cons 1 (cons 2 (cons 3 empty)))
                                             empty)
    

    我们知道我们肯定要把“元素”放到“普通列表”的后面,得到我们想要的结果“普通列表”:

    (define (f lst)      
      (cond
        [(empty? lst) empty] ;; the empty list case
    
        [else ;; the cons case has:
              ???
    
              ;; the resulting "plain list" that we want
              (append (first lst) (cons (first (rest lst)) empty))
    
              ;; the next "list of jumping pairs"
              (rest (rest lst))   
    
              ;; that are available for us to use
              ???]))              
    

    我们还需要处理下一个“跳跃对列表”,但我们已经有办法处理它:f

    (define (f lst)      
      (cond
        [(empty? lst) empty] ;; the empty list case
    
        [else ;; the cons case has:
              ???
    
              ;; the resulting "plain list" that we want
              (append (first lst) (cons (first (rest lst)) empty))
    
              ;; the list of "plain list"
              (f (rest (rest lst)))
    
              ;; that are available for us to use
              ???]))
    

    然后我们可以返回答案:

    (define (f lst)      
      (cond
        [(empty? lst) empty] ;; the empty list case
    
        [else ;; the cons case returns
              ;; the resulting list of "plain list" that we want
              (cons (append (first lst) (cons (first (rest lst)) empty))
                    (f (rest (rest lst))))]))
    

    【讨论】:

    • 美丽而详细的解释。该程序会为格式错误的“跳跃对列表”(如(f '(x y z)))引发错误 - 不一定是坏事,但可能不是 OP 想要的。我在我的回答中提供了一个替代方案。
    【解决方案2】:

    Pattern matching(在下面使用match)对于这类问题非常有用 -

    (define (f xs)
      (match xs
        ;; '((a b) c . rest)
        [(list (list a b) c rest ...)
         (cons (list a b c)
               (f rest))]
        ;; otherwise
        [_
         empty]))
    

    define/match 为这种常见的过程风格提供了一些语法糖,让事情变得更好 -

    (define/match (f xs)
      [((list (list a b) c rest ...))
       (cons (list a b c)
             (f rest))]
      [(_)
       empty])
    

    还有一个尾递归修订版 -

    (define (f xs)
      (define/match (loop acc xs)
        [(acc (list (list a b) c rest ...))
         (loop (cons (list a b c) acc)
               rest)]
        [(acc _)
         acc])
      (reverse (loop empty xs)))
    

    每个程序的输出都是一样的 -

    (f '((a b) c (e d) f (g h) i))
    ;; '((a b c) (e d f) (g h i))
    
    (f '((a b) c))
    ;; '((a b c))
    
    (f '((a b) c x y z))
    ;; '((a b c))
    
    (f '(x y z))
    ;; '()
    
    (f '())
    ;; '()
    

    作为额外的奖励,此答案不使用昂贵的 append 操作

    【讨论】:

      【解决方案3】:

      您的代码中没有递归案例,因此它只会静态地用于 4 元素列表。您需要支持以下内容:

      (f '()) ; ==> ()
      (f '((a b c) d (e f g) h)) ; ==> (cons (append '(a b c) (list 'd)) (f '((e f g) h)))
      

      现在这需要恰好偶数个元素,并且每个奇数元素都是一个正确的列表。这并没有错,但 onw 可能希望通过类型检查或添加代码来确保它不应该发生的情况。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-08
        • 1970-01-01
        • 2023-03-29
        相关资源
        最近更新 更多