« ^ »

スコープについて考える

所要時間: 約 3分

まずはWikipediaから、その定義を確認してみる。

プログラミングにおけるスコープ(英: scope, 可視範囲)とは、ある変数や関数などの名前(識別子)を参照できる範囲のこと。通常、変数や関数が定義されたスコープの外側からは、それらの名前を用いるだけでは参照できない。このときこれらの変数や関数は「スコープ外」である、あるいは「見えない」といわれる。

出典: フリー百科事典『ウィキペディア(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)

letdefun を使って scope-test 関数を定義する。

(let ((y 7))
  (defun scope-test (x)
    (list x y)))
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 関数を定義する。

(let ((y 7))
  (defun scope-test (x)
    (list x y)))
scope-test関数を定義する。

scope-test関数に 3 を渡す時、今度は直前のletで束縛されている y の値である 5 ではなく、 scope-test 関数定義時に束縛されていた y の値である 7 が使われ、式を評価した結果は (3 7) になる。

(let ((y 5))
  (scope-test 3))

(3 7)


1

「OnLisp」 Paul Graham著、NODA, Kai訳 https://www.asahi-net.or.jp/~kc7k-nd/onlispjhtml/functions.html