此过程采用非负整数n,并按照真值表所需的特定顺序创建所有n 0或1的列表的列表。我只是想了解程序的地图部分是如何工作的。我特别感到困惑的是,如果追加,映射和对所有列表的递归调用如何在if的第二个参数中一起工作。任何帮助将非常感谢!
(define all-lists
(lambda (n)
(if (= n 0)
'(())
(append (map (lambda (k) (cons 0 k)) (all-lists (- n 1)))
(map (lambda (k) (cons 1 k)) (all-lists (- n 1)))
))))
答案 0 :(得分:1)
理解递归函数的最佳策略是尝试使用比终端函数更复杂的情况。所以,让我们试试n=1
。
在这种情况下,函数变为:
(append (map (lambda (k) (cons 0 k)) (all-lists 0))
(map (lambda (k) (cons 1 k)) (all-lists 0))
即:
(append (map (lambda (k) (cons 0 k)) '(()))
(map (lambda (k) (cons 1 k)) '(())))
因此,第一个map
将函数(lambda (k) (cons 0 k))
应用于列表'(()))
的所有元素,其中只有一个元素'()
,生成'((0))
(包含由cons
0
和空列表获得的元素的列表),并且第二个映射生成'((1))
的方式相同。
这些列表会附加在一起,生成列表'((0) (1))
,换句话说,列出所有长度为1的列表,其中包含0
和1
的所有可能组合。
对于n=2
,递归案例适用于'((0) (1))
:因此第一张地图会在所有元素之前放置0
,获取'((0 0) (0 1))
,而第二张地图生成'((1 0) (1 1))
。如果您将这两个列表附加在一起,则会获得'((0 0) (0 1) (1 0) (1 1))
,这是0
和1
的长度为2的所有可能组合的列表。
依此类推,等等......
实际上,函数没有很好地定义,因为它在每次递归时不必要地计算(all-lists (- n 1))
的值两次,因此它的工作加倍,这已经是指数。因此,通过仅计算一次该值,可以提高效率,例如以下列方式:
(define all-lists
(lambda (n)
(if (= n 0)
'(())
(let ((a (all-lists (- n 1))))
(append (map (lambda (k) (cons 0 k)) a)
(map (lambda (k) (cons 1 k)) a))))))
答案 1 :(得分:0)
将陈述与' println'分开。可以帮助理解发生的事情:
(define (all-lists n)
(if (= n 0)
'(())
(let* ((a (all-lists (- n 1)))
(ol1 (map (λ (k) (cons 0 k)) a))
(ol2 (map (λ (k) (cons 1 k)) a))
(ol (append ol1 ol2)))
(println "---------")
(println ol1)
(println ol2)
(println ol)
ol)))
(all-lists 3)
输出:
"---------"
'((0))
'((1))
'((0) (1))
"---------"
'((0 0) (0 1))
'((1 0) (1 1))
'((0 0) (0 1) (1 0) (1 1))
"---------"
'((0 0 0) (0 0 1) (0 1 0) (0 1 1))
'((1 0 0) (1 0 1) (1 1 0) (1 1 1))
'((0 0 0) (0 0 1) (0 1 0) (0 1 1) (1 0 0) (1 0 1) (1 1 0) (1 1 1))
'((0 0 0) (0 0 1) (0 1 0) (0 1 1) (1 0 0) (1 0 1) (1 1 0) (1 1 1))
人们可以清楚地看到每个步骤中的候选名单(ol1,ol2和组合ol)是如何变化的。