【问题标题】:How do collector functions work in Scheme?收集器函数在 Scheme 中是如何工作的?
【发布时间】:2019-11-25 22:56:23
【问题描述】:

我无法理解 Scheme 中收集器函数的使用。我正在使用“The Little Schemer”一书(Daniel P. Friedman 和 Matthias Felleisen 着)。一个带有一些解释的综合示例将对我有很大帮助。使用收集器函数的函数示例如下 sn-p:

(define identity
  (lambda (l col)
    (cond
      ((null? l) (col '()))
      (else (identity
              (cdr l)
              (lambda (newl)
                (col (cons (car l) newl))))))))

... 示例调用为(identity '(a b c) self)self-function(define self (lambda (x) x))identity 函数返回给定列表l,因此给定调用的输出将是(a b c)。使用的确切语言是 R5RS Legacy-language。

【问题讨论】:

标签: scheme lisp racket continuation-passing the-little-schemer


【解决方案1】:

鉴于identity 定义中如何定义这些“收集器”函数,调用

(identity xs col)

对于任何列表xs和一些“收集器”函数col,相当于调用

(col xs)

所以 same 列表将被“返回”,即传递给它的参数“收集器”/延续函数col。这解释了它的名字,identity,那么。

为了比较,reverse 可以编码为

(define reverse     ; to be called as e.g. (reverse l display)
  (lambda (l col)
    (cond
      ((null? l) (col '()))        ; a reversed empty list is empty
      (else (reverse (cdr l)       ; a reversed (cdr l) is newl --
                     (lambda (newl)    ; what shall I do with it when it's ready?
                       (col            ; append (car l) at its end and let col
                          (append newl                           ; deal with it!
                                  (list (car l))))))))))

这种编程风格被称为continuation-passing style:每个函数都传递了一个“延续”,假设它将传递其余计算的结果,因此原始延续/收集器函数将传递给最终的结果。每个收集器的参数代表它将收到的未来“结果”,然后收集器函数本身指定如何处理它然后

不要对术语感到困惑:这些函数不是 call/cc 函数捕获的“延续”,它们是普通的 Scheme 函数,代表“接下来要做什么”。

定义可以理解为

identity :
  to transform a list xs 
        with a collector function col,
    is 
    | to call (col xs)                              , if xs is empty, or
    | to transform (cdr xs)  
        with a new collector function col2  
        such that
              (col2 r)  =  (col (cons (car xs) r))  , otherwise.

(或者我们可以把它写成伪代码)

(identity list col)  =
  | empty? list           ->  (col list)
  | match? list (x . xs)  ->  (identity xs col2)
                                where 
                                (col2 r)  =  (col (cons x r))

col2 通过将(cons x r) 传递给前一个处理程序col 来处理其参数r。这意味着r 被转换为(cons x r),但不是作为一个值返回,而是输入col 以进行进一步处理。因此,我们通过将新值 (cons x r) 传递给前一个“收集器”来“返回”它。

一个示例调用,作为说明:

(identity (list 1 2 3) display)     

= (identity (list 2 3) k1)
      ; k1 =  (lambda (r1) (display (cons 1 r1)))           =  display ° {cons 1}

= (identity (list 3)  k2)
      ; k2 =  (lambda (r2) (k1 (cons 2 r2)))                     =  k1 ° {cons 2} 

= (identity (list )  k3)
      ; k3 =  (lambda (r3) (k2 (cons 3 r3)))                     =  k2 ° {cons 3} 

= (k3 '())                        ; (((display ° {cons 1}) ° {cons 2}) ° {cons 3}) []

= (k2 (cons 3 '()))                    ; ((display ° {cons 1}) ° {cons 2}) [3]

= (k1 (cons 2 (list 3)))                    ; (display ° {cons 1}) [2,3]

= (display (cons 1 (list 2 3)))                  ; display [1,2,3]

= (display (list 1 2 3))

更新:在我最近喜欢使用的模式匹配伪代码中,我们可以编写

identity []        col  =  col []
identity [a, ...d] col  =  identity d ( newl =>  col [a, ...newl] )

reverse  []        col  =  col []
reverse  [a, ...d] col  =  reverse  d ( newl =>  col [...newl, a] )

希望它在视觉上非常明显,几乎不需要解释!

【讨论】:

  • 我是否认为这只是某种惰性执行结构?
  • 不一定。但是如果你把它放在另一个 lambda 后面,任何事情都可能是懒惰的。你在这里负责,继续传球。 “LiSP in Small Pieces”一书在其关于指称语义的章节中广泛使用它,以提供各种实现,IIRC。
【解决方案2】:

我添加了第二个答案,希望它可以澄清剩下的疑问,以防你有任何疑问(因为缺少“接受”标记表明)。支持>

在 Gerald J. Sussman 的声音中,正如在 SICP 讲座中听到/看到的那样,互联网上到处都有视频,我们可以边写边读,

(define identity

“身份”被定义为

  (lambda

那个函数,当给定时

           (l col)

两个参数,lcol,将

    (cond
      ((null? l)

-- 如果(null? l) 为真--

  • 好的,这意味着l是一个列表,NB

                   (col '()))
    

返回表达式(col '())的值

  • 好的,现在这意味着 col 是一个函数,需要一个参数,可能是一个空列表,
      (else (identity (cdr l)

否则它将使用更新的值进行尾递归调用,一个是(cdr l)

                      (lambda (newl)
                        (col (cons (car l) newl)))))))

和另一个新构造的函数,这样当 it 将使用其参数 newl 调用时(一个列表,正如 col 所期望的那样——因为它出现在相同的角色,它必须遵循相同的约定),将依次调用函数col,并使用由(car l) 前缀列表newl 产生的非空 列表。

因此,identity 这个函数遵循等式

( identity   (cons (car l) (cdr l))           col                        )
==
( identity       (cdr l)     (lambda (newl)  (col  (cons (car l) newl))) )

( identity   '()   col )
==
( col        '()       )

描述一个迭代过程,即转换函数调用的过程

(identity [a,      b,      c, ...,    n]    col      )

进入通话

(col
     (cons a (cons b (cons c ... (cons n '()) ... ))))

重新创建完全相同的列表,然后将其作为参数提供给已提供的函数col

【讨论】:

    猜你喜欢
    • 2018-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-01
    • 1970-01-01
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多