まずはWikipediaから、その定義を確認してみる。
まあ、だいたい理解できる。ただ本当に理解できているかというと、スコープと一言でも種類がいくつかある。動的スコープ、静的スコープ、字句スコープに分類できる。
概ねは理解しているが、良い説明ができる程理解しているかと言われると疑問だった。On Lispの邦訳1を読んでいると、良い説明を見つけた。静的スコープと字句スコープは、同じものだと理解していたが、実は厳密には異なるらしい。このあたりは、まだ理解できていない。ここでは一旦静的スコープは忘れて、動的スコープ、字句スコープについて考える事にする。
On Lispの邦訳ではCommoLispについて記述している。以下の例は、その例とほぼ同じ内容を扱っている。ただし、僕はEmacsユーザだからEmacs Lispで同じ内容を説明してみようと思う。
Emacs Lispでは、これらの仕組みを lexical-binding
変数によって切り変える事ができる。非NILだと字句スコープ、NILだと動的スコープになる。 lexical-binding
の初期値は、 Emacs 27.1
から t
となった。
先ずは動的スコープから見る。
(setq lexical-binding nil)
let
と defun
を使って scope-test
関数を定義する。
scope-test関数に 3
を渡す時、どのように評価されるだろうか。
(let ((y 5))
(scope-test 3))
scope-test
は、引数と y
をリストにして返す訳だが、 y
はどんな値だろう。直前のletで束縛されている 5
だろうか、それとも scope-test
関数の定義時(呼び出し時ではなく)にletで束縛されている 7
だろうか。
動的スコープでは y
の値は 5
、式を評価した結果は (3 5)
となる。
(let ((y 5))
(scope-test 3))
(3 5)
次に字句スコープを見る。
(setq lexical-binding t)
再度 scope-test
関数を定義する。
scope-test関数に 3
を渡す時、今度は直前のletで束縛されている y
の値である 5
ではなく、 scope-test
関数定義時に束縛されていた y
の値である 7
が使われ、式を評価した結果は (3 7)
になる。
(let ((y 5))
(scope-test 3))
(3 7)
参考
「OnLisp」 Paul Graham著、NODA, Kai訳 https://www.asahi-net.or.jp/~kc7k-nd/onlispjhtml/functions.html