コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

「静的スコープ」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
編集の要約なし
Cewbot (会話 | 投稿記録)
m Bot作業依頼: sourceタグをsyntaxhighlightタグに置換 (Category:非推奨のsourceタグを使用しているページ) - log
7行目: 7行目:


[[疑似コード]]による例を挙げる。
[[疑似コード]]による例を挙げる。
<source lang="javascript">
<syntaxhighlight lang="javascript">
A {
A {
var x;
var x;
23行目: 23行目:
x = f(42);
x = f(42);
}
}
</syntaxhighlight>
</source>
ブロック<code>A</code>で定義されている変数<code>x</code>とブロック<code>B</code>で定義されている変数<code>x</code>は同じ識別子を持つが、ブロックが異なるため実体は別である。また、ブロック<code>B</code>からは、さらに内側のブロック<code>C</code>で定義されている変数を参照することはできない。逆にブロック<code>C</code>からはブロック<code>B</code>で定義されている変数<code>x</code>とブロック<code>C</code>で定義されている変数<code>y</code>が参照可能である。
ブロック<code>A</code>で定義されている変数<code>x</code>とブロック<code>B</code>で定義されている変数<code>x</code>は同じ識別子を持つが、ブロックが異なるため実体は別である。また、ブロック<code>B</code>からは、さらに内側のブロック<code>C</code>で定義されている変数を参照することはできない。逆にブロック<code>C</code>からはブロック<code>B</code>で定義されている変数<code>x</code>とブロック<code>C</code>で定義されている変数<code>y</code>が参照可能である。


36行目: 36行目:
== Common Lispにおける例 ==
== Common Lispにおける例 ==
[[Common Lisp]]は、静的スコープを一般的なルールとし、[[動的スコープ]]の名前については明示が必要である。
[[Common Lisp]]は、静的スコープを一般的なルールとし、[[動的スコープ]]の名前については明示が必要である。
<source lang="lisp">
<syntaxhighlight lang="lisp">
(defvar *a*)
(defvar *a*)
;; *a* を動的スコープで値なしで宣言する。
;; *a* を動的スコープで値なしで宣言する。
80行目: 80行目:
;; その時点での最も外側の変数を指すことになる。
;; その時点での最も外側の変数を指すことになる。
;; 静的スコープに束縛された *a* = 3 はlambdaの中に保存されない
;; 静的スコープに束縛された *a* = 3 はlambdaの中に保存されない
</syntaxhighlight>
</source>


== 脚注 ==
== 脚注 ==

2020年7月5日 (日) 22:42時点における版

静的スコープ(せいてきスコープ、: static scope)とは、プログラミング言語におけるスコープの一種。字句のみから決定できるため、字句スコープまたはレキシカルスコープ (lexical scope) ともいう[1]

概要

まず、一般的なローカル変数のスコープについて考える。

ブロックなどの構造を持つプログラミング言語では、あるブロックの内側のローカル変数は、そのブロックの外側からは「見えない」というものが多い。ただし、以前のJavaScript (ECMAScript) のように、サポートされるのは関数内ローカルのみで、ブロックローカルというスコープは無いものもある[2]

疑似コードによる例を挙げる。

A {
    var x;
}

B {
    var x; // A内のxとは別物
    var f;

    C {
        var y; // Cの内側からしか見えない
        y = rand();
        f = function(z) { return y + z; };
    }
    x = f(42);
}

ブロックAで定義されている変数xとブロックBで定義されている変数xは同じ識別子を持つが、ブロックが異なるため実体は別である。また、ブロックBからは、さらに内側のブロックCで定義されている変数を参照することはできない。逆にブロックCからはブロックBで定義されている変数xとブロックCで定義されている変数yが参照可能である。

以上のようなスコープはローカル変数として一般的なものである。しかし、上記の疑似コード中にある f = function(z) { return y + z; }; のように、スコープ内にある手続きオブジェクトクロージャ)によって、そのスコープ内における束縛を外部に持ち出すといったような(ことが可能な言語の)場合に、「静的スコープか否か」といったようなことが議論になる。

次の節で述べるCommon Lispでのdefvarや、Perlにおいてmyではなくlocalで宣言した変数といった動的スコープの場合は、その名前解決が、そのソースコードにおいて見えるように解決される(静的スコープ)のではなく、実行時の関数呼び出しの経路(コールスタック)から、ひとつひとつ呼出元をたどるようにして行われる(詳細は「動的スコープ」の記事を参照)。

それに対し静的スコープの場合は、手続きオブジェクトがクロージャによって実装されているなどのようにして、そのスコープにおける束縛が手続きオブジェクトに「ひっ付いて」おり、コールスタックによってではなく、手続きオブジェクトが作られた場所のスコープで名前が解決される(「コールスタック」の記事中の「ルーチンの入れ子における静的スコープサポート」という記述も参照)。

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の中に保存されない

脚注

  1. ^ lexical は「字句の」「語彙の」といった意味を持つ英語の形容詞。
  2. ^ ECMAScript 6 からは、キーワードvarで宣言した関数内ローカルな変数の他に、letで宣言した変数はブロックローカルになる。

関連項目