我认为围绕define 进行绑定的任何解决方案都不能以便携或安全的方式工作,因为被包装的define 要么会构造本地绑定(主体中的前导形式),要么是非法的(非前导正文中的表格),尽管我很乐意让一个符合 Scheme 标准的人指出我错在哪里。
我认为像这种有点讨厌的黑客攻击应该可以工作。
(begin
(define inc undefined)
(define dec undefined)
(let ((x 3))
(set! inc (lambda (y)
(set! x (+ x y))
x))
(set! dec (lambda (y)
(set! x (- x y))
x))))
在这里,我依赖于一个名为 undefined 的常量,这意味着“尚未正确定义”:Racket 在 racket/undefined 中提供了这个,但它通常可以是任何东西:你可以在某个地方说
(define undefined 'undefined)
例如。
诀窍是用占位符值在顶层定义你想要的东西,然后在let 中分配给它们。
我确信可以定义一个宏,它可以扩展为类似的东西(这就是为什么我将整个东西放在begin 中):我没有这样做,因为它很繁琐,而且我使用 Racket 所以我可以' t 轻松地在其中编写旧式 Lisp 宏。
请注意,现代方案中显而易见的方法是使用define-values:
(define-values (x y) (let (...) (values ...)))
做你想做的事。如另一个答案中所述,如果您只有多个值,您可以将 define-values 实现为宏。但是,如果您根本没有多个值,那么您可以使用基于 list 结果定义的东西:
(define-list (x y) (let (...) (list ...)))
以下是该宏的两个粗略变体:第一个使用 Racket 的原生宏:
(require racket/undefined)
(define-syntax define-list
(syntax-rules ()
[(define-list () expr)
(let ((results expr))
(unless (zero? (length results))
(error "need an empty list"))
(void))]
[(define-list (name ...) expr)
(begin
(define name undefined)
...
(let ([results expr])
(unless (= (length results)
(length '(name ...)))
(error "wrong number of values"))
(set! name (begin0
(car results)
(set! results (cdr results))))
...))]))
而第二个在 Racket 中使用不卫生的宏:
(require compatibility/defmacro
racket/undefined)
(define-macro (define-list names expr)
`(begin
,@(let loop ([ntail names] [defs '()])
(if (null? ntail)
(reverse defs)
(loop (cdr ntail)
(cons `(define ,(car ntail) undefined) defs))))
(let ([results ,expr])
(unless (= (length results)
(length ',names))
(error "wrong number of values"))
,@(let loop ([ntail names] [i 0] [assignments '()])
(if (null? ntail)
(reverse assignments)
(loop (cdr ntail) (+ i 1)
(cons `(set! ,(car ntail) (list-ref results ,i))
assignments)))))))
请注意,这些都没有经过测试,我需要花一点时间说服自己第二个足够卫生。
但是有了这些:
> (define-list (inc dec)
(let ([i 0])
(list
(lambda ()
(set! i (+ i 1))
i)
(lambda ()
(set! i (- i 1))
i))))
> inc
#<procedure>
> (inc)
1
> (dec)
0
> (dec)
-1
>