Recursion 解释《小阴谋家》第137页的延续例子
有关守则如下:Recursion 解释《小阴谋家》第137页的延续例子,recursion,lambda,scheme,continuation-passing,the-little-schemer,Recursion,Lambda,Scheme,Continuation Passing,The Little Schemer,有关守则如下: (define multirember&co (lambda (a lat col) (cond ((null? lat) (col (quote ()) (quote ()))) ((eq? (car lat) a) (multirember&co a (cdr lat) (lambda (newlat seen)
(define multirember&co
(lambda (a lat col)
(cond
((null? lat)
(col (quote ()) (quote ())))
((eq? (car lat) a)
(multirember&co a
(cdr lat)
(lambda (newlat seen)
(col newlat
(cons (car lat) seen)))))
(else
(multirember&co a
(cdr lat)
(lambda (newlat seen)
(col (cons (car lat) newlat)
seen))))))
我整天都在盯着这个,但我似乎不太明白。当您在函数上重复时,您正在重新定义col
,但在示例中,它们似乎使用了原始定义。为什么它不会改变。如果不传入参数newlat
和seen
,您如何在它上重复出现
我的问题很难解释,因为我好像漏了一块。如果有人能比这本书更明确地介绍一下,我也许能理解它是如何工作的;也许这会有帮助。:-)为了简单起见,我将使用
list
作为收集器/延续器,它将返回一个带有参数的列表到延续器
(multirember&co 'foo '(foo bar) list)
一开始,
a = 'foo
lat = '(foo bar)
col = list
在第一次迭代中,(eq?(car lat)a)
条件匹配,因为lat
不是空的,lat
的第一个元素是'foo
。这将设置到multirember&co
的下一个递归:
a = 'foo
lat = '(bar)
col = (lambda (newlat seen)
(list newlat (cons 'foo seen))
在下一次迭代中,else
匹配:因为lat
不是空的,lat
的第一个元素是'bar
(而不是'foo
)。因此,对于下一个递归,我们有:
a = 'foo
lat = '()
col = (lambda (newlat seen)
((lambda (newlat seen)
(list newlat (cons 'foo seen)))
(cons 'bar newlat)
seen))
为了便于人类阅读(并避免混淆),我们可以重命名参数(由于词法范围),而不改变程序的语义:
col = (lambda (newlat1 seen1)
((lambda (newlat2 seen2)
(list newlat2 (cons 'foo seen2)))
(cons 'bar newlat1)
seen1))
最后,(null?lat)
子句匹配,因为lat
现在为空。所以我们打电话
(col '() '())
扩展至:
((lambda (newlat1 seen1)
((lambda (newlat2 seen2)
(list newlat2 (cons 'foo seen2)))
(cons 'bar newlat1)
seen1))
'() '())
其中(当替换newlat1='()
和seen1='()
时)变为
或者(评估(cons'bar'())
)
现在,替换值newlat2='(bar)
和seen2='()
,我们得到
(list '(bar) (cons 'foo '()))
或者换句话说,
(list '(bar) '(foo))
给出我们的最终结果
'((bar) (foo))
我在这里找到了一个很好的答案: 我也一直在努力解决这个问题。关键是要理解词法范围(对我来说,是Javascript)和传递给multirember&co的eq而非eq分支的内部函数。理解这一点,你就会理解整个过程。上面的链接是什么(http://www.michaelharrison.ws/weblog/?p=34)很好地解释了整个练习是如何避免命令式编程(C、Java)需要声明两个“holder”或“collector”变量(或列表、向量)的显式存储在内存中,以便在遍历列表时捕获答案。由于FP语言方案使用了continuation,当您一步一步地(草莓、金枪鱼和箭鱼)进入任何单独创建的“篮子”时,您不会“推”测试结果;相反,在发送适当的考虑函数时,您会考虑两个列表——一个用于eq?是的,另一个是eq?false--通过递归。最后是第三个col函数,在TLS的第一个示例中,该函数是“a-friend”,它询问为保存所有匹配项而构建的列表是否为空(null?)。然后,TLS要求您再次“运行”multirember&co,使用一个新的“last”col,该col仅询问包含所有“not tuna”原子的列表它包含多少(“last friend”)。因此,有两个“第一类”函数用于处理收集任务,即构建两个单独的列表,然后在递归展开结束时,原始列(“a-friend”)问最后一个问题。也许“multirember&co”这个名字并不是最伟大的名字,因为它确实不会重建除去原子的列表;相反,它构建了两个独立的列表——它们永远不会显示——然后应用最后一个列(a-friend或last friend)。显示#t或#f或“非金枪鱼”列表的长度 以下是一些输出:
> (multirember&co 'tuna '(and tuna) a-friend)
#f
> (multirember&co 'tuna '(and not) a-friend)
#t
下面是一个col,用于返回不匹配的列表:
(define list-not (lambda (x y) x))
及其用途:
> (multirember&co 'tuna '(and not) list-not)
(and not)
我希望本演练有帮助 正如Chris所建议的,我将newlat/seen重命名为n/s,并添加了一个索引。 这本书给函数起了可怕的名字(a-friend new friend latest fried),所以我只保留了L(代表lambda)和定义
multirember&co 'tuna '(strawberries tuna and swordfish) a-friend)
multirember&co 'tuna '(tuna and swordfish) (L(n1 s1)(a-friend (cons 'strawberries n1) s1))
multirember&co 'tuna '(and swordfish) (L(n2 s2)((L(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2))
multirember&co 'tuna '(swordfish) (L(n3 s3)((L(n2 s2)((L(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2)) (cons 'and n3) s3))
multirember&co 'tuna '() (L(n4 s4)((L(n3 s3)((L(n2 s2)((L(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2)) (cons 'and n3) s3)) (cons 'swordfish n4) s4))
((lambda(n4 s4)((lambda(n3 s3)((lambda(n2 s2)((lambda(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2))) (cons 'and n3) s3)) (cons 'swordfish n4) s4)) '() '())
((lambda(n3 s3)((lambda(n2 s2)((lambda(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2))) (cons 'and n3) s3)) '(swordfish) '())
((lambda(n2 s2)((lambda(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2))) '(and swordfish) '())
((lambda(n1 s1)(a-friend (cons 'strawberries n1) s1)) '(and swordfish) '(tuna))
(a-friend '(strawberries and swordfish) '(tuna))
代码不会像通常那样生成解决方案,但它会生成一个计算解决方案的代码,就像您使用低级操作(如
cons
,+
,-
)而不是使用高级累加器或过滤器来生成树一样
这就是为什么很难说过程是迭代的还是递归的,因为根据迭代过程的定义,它们对局部状态使用有限的内存。然而,这种进程使用了大量内存,但这是在环境中分配的,而不是在本地参数中分配的
首先,我复制了这里的代码,以便在不滚动太多的情况下查看通信:
(define multirember&co
(lambda (a lat col)
(cond
((null? lat)
(col (quote ()) (quote ())))
((eq? (car lat) a)
(multirember&co a
(cdr lat)
(lambda (newlat seen)
(col newlat
(cons (car lat) seen)))))
(else
(multirember&co a
(cdr lat)
(lambda (newlat seen)
(col (cons (car lat) newlat)
seen)))))))
让我们试着把问题分开,看看到底发生了什么
- 案例1:
这是一个微不足道的例子,它从不循环 现在有一些有趣的例子:
- 案例2:
在本例中,流程生成此代码作为结果,并最终对其求值。注意,在本地,它仍然是尾部递归的,但在全局,它是一个递归过程,它需要内存,不是通过分配一些数据结构,而是通过让计算器只分配环境帧。每个循环通过添加一个新帧来加深环境
- 案例3
这将在另一个分支上生成代码,该分支将结果累积到另一个变量中
所有其他案例都是这3个案例中的1个案例的组合,通过添加一个新的层,每个案例的行为都很清楚。我一直在努力了解
multirember&co
内部发生了什么。问题是,当我认为我已经得到它的时候——下一个任务/例子证明我没有
帮助我的是把正在发生的事情的视觉表现组合起来
multirember&co 'tuna '(strawberries tuna and swordfish) a-friend)
multirember&co 'tuna '(tuna and swordfish) (L(n1 s1)(a-friend (cons 'strawberries n1) s1))
multirember&co 'tuna '(and swordfish) (L(n2 s2)((L(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2))
multirember&co 'tuna '(swordfish) (L(n3 s3)((L(n2 s2)((L(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2)) (cons 'and n3) s3))
multirember&co 'tuna '() (L(n4 s4)((L(n3 s3)((L(n2 s2)((L(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2)) (cons 'and n3) s3)) (cons 'swordfish n4) s4))
((lambda(n4 s4)((lambda(n3 s3)((lambda(n2 s2)((lambda(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2))) (cons 'and n3) s3)) (cons 'swordfish n4) s4)) '() '())
((lambda(n3 s3)((lambda(n2 s2)((lambda(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2))) (cons 'and n3) s3)) '(swordfish) '())
((lambda(n2 s2)((lambda(n1 s1)(a-friend (cons 'strawberries n1) s1)) n2 (cons 'tuna s2))) '(and swordfish) '())
((lambda(n1 s1)(a-friend (cons 'strawberries n1) s1)) '(and swordfish) '(tuna))
(a-friend '(strawberries and swordfish) '(tuna))
(define multirember&co
(lambda (a lat col)
(cond
((null? lat)
(col (quote ()) (quote ())))
((eq? (car lat) a)
(multirember&co a
(cdr lat)
(lambda (newlat seen)
(col newlat
(cons (car lat) seen)))))
(else
(multirember&co a
(cdr lat)
(lambda (newlat seen)
(col (cons (car lat) newlat)
seen)))))))
(multirember&co 'a
'()
(lambda (x y) (list x y)))
is the same as
(let ((col (lambda (x y) (list x y))))
(col '() '()))
(multirember&co 'a
'(x)
(lambda (x y) (list x y)))
is the same as
(let ((col
(let ((col (lambda (x y) (list x y)))
(lat '(x))
(a 'a))
(lambda (newlat seen)
(col (cons (car lat) newlat)
seen)))))
(col '() '()))
(multirember&co 'a
'(a)
(lambda (x y) (list x y)))
is the same as
(let ((col
(let ((col (lambda (x y) (list x y)))
(lat '(a))
(a 'a))
(lambda (newlat seen)
(col newlat
(cons (car lat) seen))))))
(col '() '()))
multirember&Co a lat col
= col [] [] , IF lat == []
= multirember&Co a (cdr lat)
( newlat seen =>
col newlat
(cons (car lat) seen) ) , IF (car lat) == a
= multirember&Co a (cdr lat)
( newlat seen =>
col (cons (car lat) newlat)
seen ) , OTHERWISE
multirember&Co = g where
g a [b, ...lat] col | b == a = g a lat ( n s => col n [b, ...s] )
| else = g a lat ( n s => col [b, ...n] s )
g a [] col = col [] []
(g 1 [ 2, 1, 3, 1, 4, 5 ] col)
(col [ 2, ...[3, ...[4, ...[5, ...[]]]]]
[ 1, ...[1, ...[]] ])
(col [ 2, 3, 4, 5 ]
[ 1, 1 ])
multirember&Co a lat col = g a lat id id where
id x = x ; identity function
(f ∘ g) x = f (g x) ; function composition
g a [b, ...lat] c d
| b == a = g a lat c (d ∘ (x => cons b x)) ; (d ∘ {cons b})
| else = g a lat (c ∘ (x => cons b x)) d ; (c ∘ {cons b})
g a [] c d = col (c []) (d [])
multirember&Co 1 [ 2, 1, 3, 1, 4, 5 ] col
=
col (((((id ∘ {cons 2}) ∘ {cons 3}) ∘ {cons 4}) ∘ {cons 5}) []) ; { } is for
( ( (id ∘ {cons 1}) ∘ {cons 1} ) []) ; partial application
=
col (id (cons 2 (cons 3 (cons 4 (cons 5 [])))))
(id (cons 1 (cons 1 []) ) )
multirember&Co a lat col
= col [ b for b in lat if (b /= a) ]
[ b for b in lat if (b == a) ]
= ( ((n,s) => col n s) ∘ {partition {/= a}} ) lat