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