该程序假设选择列表中的每个第三个原子。 注意最后一个原子' 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)
由于
答案 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