在 Scheme 中让过 lambda?
let-over-lambda in Scheme?
在 Common Lisp 中,如果我想要两个函数共享状态,我会执行一个 let over lambda,如下所示:
(let ((state 1))
(defun inc-state ()
(incf state))
(defun print-state ()
(format t "~a~%" state))
这些函数不是 let
的局部函数 - 它们是全局函数,维护对共享 state 变量的引用,该变量本身从外部不可见。例如,我可以在代码的其他地方执行以下操作:
(print-state) => 1
(inc-state) => 2
(print-state) => 2
然而,在 Scheme 中,这样的结构声明了 局部函数,从外部看不到:
(let ((state 1))
(define (print-state)
(print state))
(print-state)) => 1
(print-state) => error, no such variable print-state
我能想到的实现这种功能的唯一方法(除了在模块内使用未导出的全局变量之外)是这样的:
(define print-state #f)
(define inc-state #f)
(let ((state 1))
(set! print-state (lambda () (print state)))
(set! inc-state (lambda () (inc! state))))
Scheme 中是否有一种方法可以在不诉诸这种丑陋的变通方法的情况下编写 let-over-lambda 形式?或者我需要写一个宏来包装这种丑陋吗? (顺便说一句,我知道 letrec
,但这不是解决这个问题的方法。)
顺便说一下,我正在使用 Chicken Scheme,但我的问题应该与所有 Schemes 相关。
你可以这样做:
(define-values (inc-state print-state)
(let ((state 1))
(values
(lambda () ; inc-state
(set! state (+ 1 state)))
(lambda () ; print-state
(display state)
(newline)))))
> (print-state)
1
> (inc-state)
> (print-state)
2
> state
. . state: undefined; cannot reference an identifier before its definition
>
(输出来自 Racket,但我也在 Chicken Scheme 中对其进行了测试)
不幸的是,顶级绑定只能成为顶级绑定,并且 define
在过程中实际上只是 letrec
的语法糖。在 Chicken Scheme 中,您有一个名为 define-values
的表单,您可以在其中执行此操作:
(define-values (print-state inc-state)
(let ((state 1))
(values (lambda () (print state))
(lambda () (inc! state)))))
请注意,define-values
不是任何标准的一部分,尽管它看起来很常见。制作一个使用您用来实现它的方法的宏会很容易。对于替代解决方案,您可以 return 您调用的调度程序以访问过程:
(define dispatcher
(let ((state 1))
(lambda (msg)
(case msg
((print) (lambda () (print state)))
((inc!) (lambda () (inc! state)))))))
(define print-state (dispatcher 'print))
(define inc-state (dispatcher 'inc!))
实际上你不需要创建全局变量,因为你可以直接调用 return:
((dispatcher 'inc!))
((dispatcher 'inc!))
((dispatcher 'print)) ; ==> prints 3
在 Common Lisp 中,如果我想要两个函数共享状态,我会执行一个 let over lambda,如下所示:
(let ((state 1))
(defun inc-state ()
(incf state))
(defun print-state ()
(format t "~a~%" state))
这些函数不是 let
的局部函数 - 它们是全局函数,维护对共享 state 变量的引用,该变量本身从外部不可见。例如,我可以在代码的其他地方执行以下操作:
(print-state) => 1
(inc-state) => 2
(print-state) => 2
然而,在 Scheme 中,这样的结构声明了 局部函数,从外部看不到:
(let ((state 1))
(define (print-state)
(print state))
(print-state)) => 1
(print-state) => error, no such variable print-state
我能想到的实现这种功能的唯一方法(除了在模块内使用未导出的全局变量之外)是这样的:
(define print-state #f)
(define inc-state #f)
(let ((state 1))
(set! print-state (lambda () (print state)))
(set! inc-state (lambda () (inc! state))))
Scheme 中是否有一种方法可以在不诉诸这种丑陋的变通方法的情况下编写 let-over-lambda 形式?或者我需要写一个宏来包装这种丑陋吗? (顺便说一句,我知道 letrec
,但这不是解决这个问题的方法。)
顺便说一下,我正在使用 Chicken Scheme,但我的问题应该与所有 Schemes 相关。
你可以这样做:
(define-values (inc-state print-state)
(let ((state 1))
(values
(lambda () ; inc-state
(set! state (+ 1 state)))
(lambda () ; print-state
(display state)
(newline)))))
> (print-state)
1
> (inc-state)
> (print-state)
2
> state
. . state: undefined; cannot reference an identifier before its definition
>
(输出来自 Racket,但我也在 Chicken Scheme 中对其进行了测试)
不幸的是,顶级绑定只能成为顶级绑定,并且 define
在过程中实际上只是 letrec
的语法糖。在 Chicken Scheme 中,您有一个名为 define-values
的表单,您可以在其中执行此操作:
(define-values (print-state inc-state)
(let ((state 1))
(values (lambda () (print state))
(lambda () (inc! state)))))
请注意,define-values
不是任何标准的一部分,尽管它看起来很常见。制作一个使用您用来实现它的方法的宏会很容易。对于替代解决方案,您可以 return 您调用的调度程序以访问过程:
(define dispatcher
(let ((state 1))
(lambda (msg)
(case msg
((print) (lambda () (print state)))
((inc!) (lambda () (inc! state)))))))
(define print-state (dispatcher 'print))
(define inc-state (dispatcher 'inc!))
实际上你不需要创建全局变量,因为你可以直接调用 return:
((dispatcher 'inc!))
((dispatcher 'inc!))
((dispatcher 'print)) ; ==> prints 3