不寻常的Scheme `let`绑定,什么是`f`?

Unusual Scheme `let` binding, what is `f`?

"The Scheme Programming Language 4th Edition"第3.3节Continuations中给出了以下示例:

(define product
  (lambda (ls)
    (call/cc
      (lambda (break)
        (let f ([ls ls])
          (cond
            [(null? ls) 1]
            [(= (car ls) 0) (break 0)]
            [else (* (car ls) (f (cdr ls)))]))))))

我可以确认它在 chezscheme 中的工作原理如下:

> (product '(1 2 3 4 5))
120

上面let中的'f'是什么?为什么给定的 ls 被分配给自己?它似乎不符合我对 (let ...) 的理解,如 4.4 local binding:

中所述
syntax: (let ((var expr) ...) body1 body2 ...)

如果此处定义了“f”,我希望它在 parenthesis/square 括号内:

(let ([f some-value]) ...)

f 绑定到一个过程,该过程以 let 的主体作为主体,ls 作为参数。

http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_sec_11.16

这是'named let',语法方便。

(let f ([x y] ...)
  ...
  (f ...)
  ...)

大致等同于

(letrec ([f (λ (x ...)
              ...
              (f ...)
              ...)])
  (f y ...))

或者,在适当的情况下,到本地 define 然后拨打电话:

(define (outer ...)
  (let inner ([x y] ...)
    ...
    (inner ...)
    ...))

大致等同于

(define (outer ...)
  (define (inner x ...)
    ...
    (inner ...)
    ...)
  (inner y ...))

named let 的好处在于它将本地函数的定义和初始调用放在同一个地方。

像我这样使用 CL 的穴居人有时会使用像下面的 binding 这样的宏来实现这一点(注意这不是生产代码:它的所有错误消息都是晦涩的笑话):

(defmacro binding (name/bindings &body bindings/decls/forms)
  ;; let / named let
  (typecase name/bindings
    (list
     `(let ,name/bindings ,@bindings/decls/forms))
    (symbol
     (unless (not (null bindings/decls/forms))
       (error "a syntax"))
     (destructuring-bind (bindings . decls/forms) bindings/decls/forms
       (unless (listp bindings)
         (error "another syntax"))
       (unless (listp decls/forms)
         (error "yet another syntax"))
       (multiple-value-bind (args inits)
           (loop for binding in bindings
                 do (unless (and (listp binding)
                                 (= (length binding) 2)
                                 (symbolp (first binding)))
                      (error "a more subtle syntax"))
                 collect (first binding) into args
                 collect (second binding) into inits
                 finally (return (values args inits)))
         `(labels ((,name/bindings ,args
                     ,@decls/forms))
            (,name/bindings ,@inits)))))
    (t
     (error "yet a different syntax"))))

想到这个程序:

(define (sum lst)
  (define (helper lst acc)
    (if (null? lst)
        acc
        (helper (cdr lst) 
                (+ (car lst) acc))))
  (helper lst 0))

(sum '(1 2 3)) ; ==> 6

我们可以使用 named let 而不是定义本地过程,然后像这样使用它:

(define (sum lst-arg)
  (let helper ((lst lst-arg) (acc 0))
    (if (null? lst)
        acc
        (helper (cdr lst) 
                (+ (car lst) acc)))))

除了一些重复的命名情况外,这些代码完全相同。 lst-arg 可以同名 lst 并且 永远不会 let 中的 lst 相同。

命名为let很容易掌握。 call/cc通常需要一些成熟时间。在我开始创建自己的实现之前,我没有得到 call/cc