静的スコープ
表示
静的スコープ(せいてきスコープ、テンプレート:Lang-en-short)とは、プログラミング言語におけるスコープの一種。構文構造のみから決定できるため構文スコープ[1]ともいう。
ブロックなどの構造を持つプログラミング言語では、あるブロックの内側で定義された変数はそのブロックの外側から操作することができないのが普通である。以下の疑似コードでは
A {
var x;
}
B {
var x; // A内のxとは別物
C {
var y; // Cの内側からしか見えない
}
}
ブロックAで定義されている変数xとブロックBで定義されている変数xは同じ識別子を持つが、ブロックが異なるため実体は別である。また、ブロックBからは、さらに内側のブロックCで定義されている変数を参照することはできない。逆にブロックCからはブロックBで定義されている変数xとブロックCで定義されている変数yが参照可能である。
近代的なプログラミング言語では、コードレベルのブロックだけでなく、名前空間を導入してスコープを制限する機能が普及しつつある。静的スコープはプログラミングの安全性や、最適化が行いやすいなどの理由で構造化言語を中心に広く採用されている。静的スコープでないものの代表は動的スコープである。
Common Lispにおける例
Common Lispは、静的スコープと動的スコープを共にサポートする言語である。定められた方法で指定することで、変数を静的スコープにしたり、動的スコープにしたりすることができる。
(defvar *a*)
;; *a* を動的スコープで値なしで宣言する。
;; アスタリスクは名前の一部である。
;; defvarは、以降のそれに対する束縛が静的なものでなく、
;; 動的なものである事を保証する。
(setf *a* 5)
;; 変数 *a* を整数 5 に設定する。
(let ((*a* 3)) *a*)
; --> 3
;; 明示的にletの中で上書きされた場合、*a*は3
*a*
; --> 5
;; letの外に出るともとに戻る
(defvar func-lex)
(setf func-lex
(let ((a 3))
(lambda () a)))
;; 現在、静的スコープ内の3がlambdaの中に残っている
;; したがって、
(let ((a 5))
(funcall func-lex))
; --> 3
;; 外からaを書き換えて呼び出しても、
;; 保存された静的な束縛 a = 3 がまだ残っており、
;; それが有効になって答えは3となる
;; 一方、*a*について考えると、
(defvar func-dyn)
(setf func-dyn
(let ((*a* 3))
(lambda () *a*)))
(funcall func-dyn)
; --> 5
;; *a*は動的スコープの変数として宣言されているため、
;; lambdaの中の *a* は常に
;; その時点での最も外側の変数を指すことになる。
;; 静的スコープに束縛された *a* = 3 はlambdaの中に保存されない