4

在 Common Lisp 中,如果我想要两个函数共享状态,我会执行一个let over lambda,如下所示:

(let ((state 1))
 (defun inc-state ()
  (incf state))
 (defun print-state ()
  (format t "~a~%" state))

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

(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 有关。

4

2 回答 2

4

你可以这样做:

(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 中对其进行了测试)

于 2016-08-21T17:17:21.663 回答
4

不幸的是,顶级绑定只能成为顶级绑定,并且define在程序内部实际上只是letrec. 在 Chicken Scheme 中,您有一个名为的表单define-values,您可以在其中执行此操作:

(define-values (print-state inc-state)
  (let ((state 1))
    (values (lambda () (print state))
            (lambda () (inc! state)))))

请注意,这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!))

实际上,您不需要制作全局变量,因为您可以直接调用 return:

((dispatcher 'inc!))
((dispatcher 'inc!))
((dispatcher 'print)) ; ==> prints 3
于 2016-08-21T17:22:34.023 回答