4

Common Lisp では、2 つの関数で状態を共有したい場合、次のようにlet over ラムダを実行します。

(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))))

そのような醜い回避策に頼ることなく、 let-over-lambdaフォームを書く方法は Scheme にありますか? それとも、この醜さをラップするマクロを書く必要がありますか? (ところで、私は について知っていletrecますが、それはこの問題の解決策ではありません。)

ちなみに、私はチキンスキームを使用していますが、私の質問はすべてのスキームに関連するはずです。

4

2 に答える 2

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 に答える