【问题标题】:How to get the even elements in a list recursively如何递归地获取列表中的偶数元素
【发布时间】:2012-11-14 05:35:57
【问题描述】:

我正在尝试创建一个返回列表中偶数元素的函数。

例如:

(evens '(a b c d)) 

应该返回

(b d)

下面的代码似乎适用于包含奇数个元素的列表,但如果我给它一个包含偶数个元素的列表,那就不正确了。

例如:

(evens '(a b c d e))

将返回

(b d)

但是:

(evens '(a b c d))

会回来

(a c)

有什么想法吗?

将我的代码更改为:

(DEFINE (evens lis)
(cond
    ((null? lis) '())   
    (else (cons (cadr lis) (evens (cdr lis))))
    ))

得到一个错误,指出传递给安全车的对象不是一对?

【问题讨论】:

  • 单步执行代码,错误应该很明显。 (这是第一次迭代。)
  • 您应该添加新代码,而不是替换旧代码。现在这个问题不完整,其中一部分不再相关。
  • 一个相关的,志趣相投的answer - in F#。强烈推荐!!在 Scheme 中,您需要 cddr 函数。

标签: list scheme


【解决方案1】:

问题在于,如果您的列表有偶数个元素,则匹配 modulo 分支并且您以列表的 car 开始 consing ...因此在您的示例中,您会得到a,等等。

不过,更重要的是,您不需要对这个函数使用 length ......而且您也不应该:因为 length 在列表长度上采用线性时间,evens 现在采用二次方时间。

建议:您的程序应该在每个递归步骤中“记住”它是在“奇数”还是“偶数”位置......你怎么能做到这一点(有几种方法)?

【讨论】:

    【解决方案2】:

    您的代码几乎没有遗漏检查和一些不正确的逻辑。

    (define (evens lis)
    (cond
        ((null? lis) '())   
        ((eq? (cdr lis) '()) '()) ;missing condition
        (else (cons (cadr lis) (evens (cddr lis)))))) ; it is cddr not cdr
    

    【讨论】:

      【解决方案3】:

      我将通过注释示例来回答您的问题,希望您能真正学到一些东西,而不是仅仅获得有效的代码。实际上,假设您是 scheme 新手,查看几段代码可能会更有启发性。

      您的原始定义如下所示:

      (define (evens lis)
        (cond (;; Check: Recursion stop condition
               (null? lis)
               '())
              (;; Wrong: Calling length at each step => O(n^2)
               ;; Wrong: Assuming even element if list has even number of elements
               (= (modulo (length lis) 2) 0) 
               ;; Wrong: Recursing with the rest of the list, you'll get odds
               (cons (car lis) (evens (cdr lis)))) 
              (else
               ;; Wrong: Recursing with the rest of the list with cdr, you'll get odds
               (evens (cdr lis)))))
      

      之后,您编辑了您的问题以将其更新为如下内容:

      (define (evens lis)
        (cond (;; Check: Recursion stop condition
               (null? lis)
               '())   
              (else
               ;; Check: Building list with second element
               ;; Wrong: If lis only has 1 element,
               ;;        (cdr lis) is null and (car (cdr list)) is an error.
               (cons (cadr lis)
                     ;; Wrong: Recursing with cdr, you'll get odds
                     (evens (cdr lis))))))
      

      一种解决方案是检查列表是否至少有第二个元素:

      (define (evens lis)
        (cond (;; Check: Recursion stop condition 1
               (null? lis)
               '())
              (;; Check: Recursion stop condition 2: list of length = 1
               (null? (cdr lis))
               '())
              (else
               ;; Check: Building list with second element
               ;; The previous cond clauses have already sorted out
               ;; that lis and (cdr lis) are not null.
               (cons (cadr lis)
                     ;; Check: Recurse "the rest of the rest" of lis with cddr
                     (evens (cddr lis)))))
      

      练习:使用ifor 将这个解决方案简化为只有两个分支。

      【讨论】:

        【解决方案4】:

        过去几天,timeagain 也提出了同样的问题。这次我直接回答,直截了当:

        (define (evens lst)
          (if (or (null? lst)             ; if the list is empty 
                  (null? (cdr lst)))      ; or the list has a single element
              '()                         ; then return the empty list
              (cons (cadr lst)            ; otherwise `cons` the second element
                    (evens (cddr lst))))) ; and recursively advance two elements
        

        下面是如何首先进行一些错误检查:

        (define (find-evens lst)
          (if (list? lst)
              (evens lst)
              (error "USAGE: (find-evens [LIST])")))
        

        【讨论】:

        • 这个问题可以标记为重复吗?或者更确切地说,其他人,因为这个似乎已经引起了更多的关注和回答?
        • @PauloMadeira 我已经将此标记为重复,只应保留第一个问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-25
        • 2019-12-22
        • 1970-01-01
        • 1970-01-01
        • 2020-03-22
        • 1970-01-01
        • 2015-03-24
        相关资源
        最近更新 更多