不寻常的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
。
在"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
。