一般来说,我是 Scheme 和 Lisp 的新手,学习后我偶然发现了一种用于本地过程绑定(bind)的神秘语法:
(define mock
(lambda (s)
;; this is what I don't understand
(let splice ([l '()] [m (car s)] [r (cdr s)])
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))))
我花了一段时间才明白 splice
是一个具有 3 个参数的作用域过程。以 ML-esque 风格重写它似乎会产生类似的输出:
(define mock2
(lambda (s)
;; define `splice` first
(define splice
(lambda (la lb lc)
(append
(map (lambda (x) (cons lb x)) lc)
(if (null? lc) '()
(splice (cons lb la) (car lc) (cdr lc))))))
;; bind `splice` and its arguments together and call it with them
(let ([sp splice] [l '()] [m (car s)] [r (cdr s)])
(splice l m r))))
第二个版本有点长,看起来更命令,但是在将它与参数并行绑定(bind)之前将 splice
定义为范围内的正常过程(或者只是将它们按原样丢弃) 并且调用它看起来更理智。
问题是这两个版本可以替换吗?如果是,您能否帮助解释第一个版本在 splice 中绑定(bind)局部变量(
绑定(bind)形式?l
、m
和 r
)的语法
最佳答案
调用 splice
就像重新进入一个循环,这就是它的作用。无论如何,尾调用是一个 goto。它通常被命名为loop
,而不是为它想出一些特殊的名字。
“看起来更理智”是值得商榷的,实际上对于 Schemers 你会失去这个,因为这是一个非常流行的 Scheme 结构,称为 “named let”。它通常用 letrec
顺便说一句重写,如果/当有人想重写它时,可以更好地理解它。也可以使用内部 define
,但是为什么不首先使用 (define (mock s) ...
。
所以,重写这个的通常方法
(define mock ; or: (define (mock s) ...
(lambda (s)
(let splice ([l '()] [m (car s)] [r (cdr s)])
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))))
这是:
(define mock
(lambda (s)
(letrec ([splice (lambda (l m r) ; or: (define (splice l m r) ...
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))])
(splice '() (car s) (cdr s)))))
并以命名的 let 方式编写它可以避免在一个地方定义它并在另一个可能很远的地方调用它。无论如何,一个调用从一开始就进入了它的主体,并且命名为 let 更好地反射(reflect)了那个。
这是不言自明的。从一种形式到另一种形式的转换纯粹是语法上的,两者可以互换使用。
关于syntax - 本地过程绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47082630/