如何递归地获取列表中的偶数元素

时间:2012-11-14 05:35:57

标签: list scheme

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

例如:

(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))))
    ))

获取一个错误,说传递给safe-car的对象不是一对?

4 个答案:

答案 0 :(得分:0)

问题在于,如果您的列表中包含偶数个元素,则modulo分支会匹配,您可以使用列表的cons开始car ...因此,例如,您获得a,依此类推。

但更重要的是,您不需要使用length来执行此功能......您不应该:因为length在列表的长度中占用线性时间,{{ 1}}现在需要二次时间。

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

答案 1 :(得分:0)

您的代码缺少检查和一些不正确的逻辑。

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

答案 2 :(得分:0)

过去几天,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])")))

答案 3 :(得分:0)

我将用评论的例子回答你的问题,希望你能真正学到一些东西,而不仅仅是给出有效的代码。实际上,假设您不熟悉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将此解决方案简化为只有2个分支。