使用方案获取每个第n个原子并不会获取最后一个原子

时间:2014-04-03 02:46:10

标签: scheme

该程序假设选择列表中的每个第三个原子。 注意最后一个原子' p'应该被接受,但不是。 关于为什么没有选择最后一个原子的任何建议。

(define (every3rd lst)
 (if (or (null? lst)            
      (null? (cdr lst)))     
  '()                        
  (cons (car lst)            
        (every3rd (cdr(cdr(cdr lst)))))))


(every3rd '(a b c d e f g h i j k l m n o p))

Value 1: (a d g j m)

由于

3 个答案:

答案 0 :(得分:2)

你遗漏了几个基本案例:

(define (every3rd lst)
  (cond ((or (null? lst) (null? (cdr lst))) lst)
        ((null? (cdr (cdr lst))) (list (car lst)))
        (else (cons (car lst)
                    (every3rd (cdr (cdr (cdr lst))))))))

了解应如何处理以下案例:

(every3rd '())
=> '()

(every3rd '(a))
=> '(a)

(every3rd '(a b))
=> '(a)

(every3rd '(a b c))
=> '(a)

(every3rd '(a b c d))
=> '(a d)

(every3rd '(a b c d e f g h i j k l m n o p))
=> '(a d g j m p)

答案 1 :(得分:2)

修复代码(涵盖基本情况)

值得注意的是,Scheme定义了许多c[ad]+r函数,因此您可以使用(cdddr list)代替(cdr (cdr (cdr list)))

(cdddr '(a b c d e f g h i))
;=> (d e f g h i)

正如其他人已经指出的那样,您的代码存在的问题是它没有考虑所有基本情况。在我看来,你有两个基本案例,第二个有两个子案例:

  • 如果列表为空,则根本没有要素,因此您只能返回空列表。
  • 如果列表非空,那么至少要有一个元素,你需要接受它。但是,当你递归时,有两种可能性:
    • 有足够的元素(三个或更多),您可以获取列表的cdddr;或
    • 没有足够的元素,你所采取的元素应该是最后一个元素。

如果你假设<???>可以某种方式处理这两个子句,那么你可以拥有这个通用结构:

(define (every3rd list)
  (if (null? list)
      '()
      (cons (car list) <???>)))

由于你已经知道如何处理空列表的情况,我认为这里一个有用的方法是模糊两个子句之间的区别,并简单地说:“递归到x x如果它有一个列表是cdddr,如果没有则是空列表。“编写一个函数maybe-cdddr很容易,如果它有一个,则返回“cdddr列表,如果没有,则返回空列表”:

(define (maybe-cdddr list)
  (if (or (null? list)
          (null? (cdr list))
          (null? (cddr list)))
      '()
      (cdddr list)))
> (maybe-cdddr '(a b c d))
(d)
> (maybe-cdddr '(a b c))
()
> (maybe-cdddr '(a b))
()
> (maybe-cdddr '(a))
()
> (maybe-cdddr '())
()

现在你可以将这些结合起来得到:

(define (every3rd list)
  (if (null? list)
      '()
      (cons (car list) (every3rd (maybe-cdddr list)))))
> (every3rd '(a b c d e f g h i j k l m n o p))
(a d g j m)

更模块化的方法

首先解决更普遍的问题通常更容易。在这种情况下,从列表中取出每个 n th 元素:

(define (take-each-nth list n)
  ;; Iterate down the list, accumulating elements 
  ;; anytime that i=0.  In general, each
  ;; step decrements i by 1, but when i=0, i
  ;; is reset to n-1.
  (let recur ((list list) (i 0))
    (cond ((null? list) '())
          ((zero? i)    (cons (car list) (recur (cdr list) (- n 1))))
          (else         (recur (cdr list) (- i 1))))))
> (take-each-nth '(a b c d e f g h i j k l m n o p) 2)
(a c e g i k m o)

> (take-each-nth '(a b c d e f g h i j k l m n o p) 5)
(a f k p)

一旦你完成了这个,就很容易定义更具体的案例:

(define (every3rd list)
  (take-each-nth list 3))
> (every3rd '(a b c d e f g h i j k l m n o p))
(a d g j m)

这样做的好处是,您现在可以更轻松地改进一般情况并维护相同的接口every3rd,而无需进行任何更改。例如,take-each-nth的实现在第二种情况下在递归但非尾调用中使用了一些堆栈空间。通过使用累加器,我们可以按相反的顺序构建结果列表,并在到达列表末尾时返回它:

(define (take-each-nth list n)
  ;; This loop is like the one above, but uses an accumulator
  ;; to make all the recursive calls in tail position.  When 
  ;; i=0, a new element is added to results, and i is reset to
  ;; n-1.  If i≠0, then i is decremented and nothing is added 
  ;; to the results.  When the list is finally empty, the
  ;; results are returned in reverse order.
  (let recur ((list list) (i 0) (results '()))
    (cond ((null? list) (reverse results))
          ((zero? i)    (recur (cdr list) (- n 1) (cons (car list) results)))
          (else         (recur (cdr list) (- i 1) results)))))

答案 2 :(得分:0)

这是因为(null? '())是真的。您可以使用以下代码调试正在发生的事情

(define (every3rd lst)
 (if (begin 
      (display lst)
      (newline)
      (or (null? lst)            
        (null? (cdr lst))))
  '()                        
  (cons (car lst)            
        (every3rd (cdr(cdr(cdr lst)))))))

(every3rd '(a b c d e f g h i j k l m n o p))
(newline)    
(display (cdr '(p)))
(newline)
(display (null? '()))
(newline)
(display (null? (cdr '(p))))
(newline)

这给出了以下结果。

(a b c d e f g h i j k l m n o p)
(d e f g h i j k l m n o p)
(g h i j k l m n o p)
(j k l m n o p)
(m n o p)
(p)

()
#t
#t