動的スコープとは
動的スコープ(dynamic scope)とは、
プログラミング言語におけるスコープの一種で、変数の参照範囲がプログラムの実行時に動的に決定される仕組みです。具体的には、関数やブロックが呼び出された際に、その呼び出し元のスコープを参照します。これにより、同じ名前の変数でも、どの場所から呼び出されたかによって参照される変数が変わるという特徴があります。
動的スコープの動作
以下のような疑似コードを例に考えてみましょう。
A {
print x
}
B {
var x
call A // Aの中からxを参照することができる
}
C {
var y
call A // Aの中からxを参照することはできない
}
この例では、関数Aは変数xを出力しようとしています。関数BからAが呼び出された場合、AはBで定義された変数xを参照できます。一方、関数CからAが呼び出された場合は、Cに変数xは定義されていないため、別のスコープにあるxは参照できません。
このように、動的スコープでは、関数がどの場所から呼び出されたかによって、参照できる変数が動的に変化します。
動的スコープの問題点
動的スコープは、一見すると便利な機能のように思えますが、予期せぬ動作を引き起こしやすく、バグの原因になりやすいという問題点があります。例えば、以下の疑似コードを見てください。
var x = 123
A {
var x = 456
call C // 456と出力される
}
B {
call C // 123と出力される
}
C {
print x // 呼び出し元によって x の値が変わる
}
関数Cは変数xを出力しますが、関数Aから呼び出された場合は456、関数Bから呼び出された場合は123と、呼び出し元によって出力結果が変わってしまいます。このように、動的スコープはコードの可読性や保守性を著しく低下させる可能性があります。
動的スコープの例
動的スコープを採用している
プログラミング言語としては、初期のLISP、
Emacs Lisp、
LOGO、
Perl(`local` 宣言された変数)、
Bash(関数内で `local` 宣言された変数)などが挙げられます。
エクステントとの関係
動的スコープは、変数のスコープ(参照範囲)とエクステント(生存期間)を混同して捉えられがちです。本来、動的スコープは「動的なエクステント」を持つ変数とセットで語られるべきものです。Common Lispの標準規格書では、動的スコープは「dynamic extent」を持つ変数であり、正確には「indefinite scope and dynamic extent」と表現されるべきであると指摘されています。
動的スコープは、
静的スコープ(レキシカルスコープ)と比較されることがあります。
静的スコープは、変数の参照範囲がコードの記述位置によって決定されるのに対し、動的スコープは実行時の呼び出し関係によって決定されます。動的スコープは、
静的スコープの「バグのある実装」のように見えることもありますが、実際には異なる概念です。
初期のLISPが動的スコープを採用したことについて、
ジョン・マッカーシーは後に「本来は
静的スコープが欲しかったが、動的スコープになってしまった」と回想しています。
まとめ
動的スコープは、プログラムの実行時に変数の参照範囲が変化するという特徴を持つスコープです。強力な機能である一方、バグの原因になりやすく、コードの可読性や保守性を低下させる可能性があるため、使用には注意が必要です。
静的スコープとの違いを理解し、適切に使い分けることが重要です。
参考資料
西尾泰和『コーディングを支える技術 ~成り立ちから学ぶプログラミング作法』
関連項目
名前空間