在Scheme中让-over-lambda?

Sod*_*hty 5 lambda scheme chicken-scheme let-over-lambda

在 Common Lisp 中,如果我希望两个函数共享状态,我将执行let over lambda,如下所示:

(let ((state 1))
 (defun inc-state ()
  (incf state))
 (defun print-state ()
  (format t "~a~%" state))
Run Code Online (Sandbox Code Playgroud)

这些函数不是本地函数let- 它们是全局函数,维护对共享状态变量的引用,该变量本身从外部不可见。例如,我可以在代码中的其他位置执行以下操作:

(print-state)       => 1
(inc-state)         => 2
(print-state)       => 2
Run Code Online (Sandbox Code Playgroud)

然而,在Scheme中,这样的构造声明了从外部不可见的局部函数:

(let ((state 1))
 (define (print-state)
  (print state))

 (print-state))     => 1

(print-state)       => error, no such variable print-state
Run Code Online (Sandbox Code Playgroud)

我认为实现这种功能的唯一方法(除了在模块内使用未导出的全局变量之外)是这样的:

(define print-state #f)
(define inc-state #f)

(let ((state 1))
 (set! print-state (lambda () (print state)))
 (set! inc-state (lambda () (inc! state))))
Run Code Online (Sandbox Code Playgroud)

在Scheme中是否有一种方法可以编写let-over-lambda形式而无需诉诸如此丑陋的解决方法?或者我需要编写一个宏来包装这种丑陋吗?(顺便说一句,我知道letrec,这不是这个问题的解决方案。)

顺便说一句,我正在使用 Chicken Scheme,但我的问题应该与所有计划相关。

Syl*_*ter 5

不幸的是,顶级绑定只能是顶级的,并且define在过程内部实际上只是letrec. 在“鸡计划”中,您有一个名为的表格,define-values您可以在其中执行此操作:

(define-values (print-state inc-state)
  (let ((state 1))
    (values (lambda () (print state))
            (lambda () (inc! state)))))
Run Code Online (Sandbox Code Playgroud)

请注意,这define-values不是任何标准的一部分,尽管它似乎是常见的形式。不过,使用您用来实现它的方法来创建一个宏会很容易。对于替代解决方案,您可以返回一个调度程序,您可以调用该调度程序来访问过程:

(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!))
Run Code Online (Sandbox Code Playgroud)

实际上,您不需要创建全局变量,因为您可以直接调用返回:

((dispatcher 'inc!))
((dispatcher 'inc!))
((dispatcher 'print)) ; ==> prints 3
Run Code Online (Sandbox Code Playgroud)