2008年02月15日

レキシカルスコープ(lexical scope、静的スコープ、構文スコープ)・ダイナミックスコープ(dynamic scope、動的スコープ)が間違っていた件

昔の記事を見てみたら、ずいぶんと間違っていたので書きなおしてみます。

動的スコープ、静的スコープ両方の変数が使用できるperlが一番わかりやすいようです。
localで動的スコープ、myで静的スコープの変数が作成できます。myはperl5から使えるそうです。

#!/usr/bin/perl

sub sub_parent {
local $dynamic_var = 10;
my $lexical_var = 20;
&sub_child;
}

sub sub_child {
print "dynamic_var = $dynamic_var\n";
print "lexical_var = $lexical_var\n";
}

&sub_parent;

-> dynamic_var = 10
lexical_var =


動的スコープはそのコードが実行される際に親のスコープを参照できます。
上の例で、sub_child内で$dynamic_varは、呼出元sub_parentの値を参照できるので、
dynamic_var = 10

と表示されています。

静的スコープはそのコードの構文のみでスコープが決定します。
上の例で、sub_child内で$lexical_varは、呼出元の値を参照しないので
lexical_var = 

と何も表示されていません。


今度はこんなにしてみました。

#!/usr/bin/perl

sub sub_parent {
local $dynamic_var = 10;
my $lexical_var = 20;

sub sub_child {
print "dynamic_var = $dynamic_var\n";
print "lexical_var = $lexical_var\n";
}

sub sub_other {
local $dynamic_var = 100;
my $lexical_var = 200;
&sub_child;
}

&sub_other;
}

&sub_parent;

-> dynamic_var = 100
lexical_var = 20


sub_parent内にsub_childとsub_otherというサブルーチンを作りました。
sub_perentを呼ぶと、sub_otherを呼び出します。
sub_otherでは、$dynamic_varを100に、$lexical_varを200にしてからsub_childを呼び出しています。
動的スコープの変数$dynamic_varは
dynamic_var = 100

と10->100に変更されて表示されていますが、
静的スコープの変数$lexical_varは
lexical_var = 20

と20->200に変更されていません。

これはコードを先頭から構文解析する時に(ホント?)、$lexical_varの値が決定しているからです。


perlでもサブルーチンを返すサブルーチンが書けたので、こんなにしてみました。

#!/usr/bin/perl

sub hoe {
local $a = @_[0];
my $b = @_[1];
return sub {
print "a = $a\n";
print "b = $b\n";
}
}

sub hoehoe {
$pfunc = @_[0];

local $a = 1000;
my $b = 2000;
&$pfunc;
}

$f1 = &hoe(10, 20);
$f2 = &hoe(100, 200);

&hoehoe($f2);
-> a = 1000
b = 200

&hoehoe($f1);
-> a = 1000
b = 20

$aは動的スコープの変数なので、hoehoe内で呼ばれる時に、hoehoe内で与えられた値1000を表示しています。
$bは静的スコープの変数なので、hoeでサブルーチンが作成された時の値@_[1]つまりhoeの2番目の引数の値20または200を表示しています。

また、$f1、$f2の順でサブルーチンが作成されていますが、それぞれ作成された時の値20、200を覚えています。これはクロージャの話ですね。
静的スコープでリテラルでない値を参照する時の話になると、クロージャとは切り離せないのでしょうか?リテラルでの話の場合だとstatic変数って事じゃないの?って思ってしまうのです。


JavaScriptの事。JavaScriptは静的スコープです。

function hoe(a) {
return function() {
alert(a);
}
}
var f1 = hoe(10);
var f2 = hoe(20);
f2(); -> 20
f1(); -> 10

こんなです。


ここからはいろいろ間違っていた話。

(define a 100)
(define bar (display a))
(define foo (let ((a 10)) bar))

(foo) -> 100

コレはエラーになっちゃいますね。
少なくともコウですね。

(define a 100)
(define (bar) (display a))
(define (foo) (let ((a 10)) bar))

((foo)) -> 100

letの事がわからなくなっちゃったので、保留…。

JavaScriptの間違っていた(?)話。
確かに、VBとかCとかは動的スコープ。C#はきっと静的スコープも使えるようになっているのではないかと。最近の事はしらない。
で、JavaScriptは動的スコープではない。静的スコープです。
で、コード

a = 100;
function bar() {
alert(a);
}
function foo() {
a = 10;
bar();
}

foo(); -> 10

は確かに10になるけど、これは単にaが全部グローバル変数を参照しているだけです。
このfoo();の後にalert(a);と書くと10です。
JavaScriptの変数のスコープは関数なので、

var a = 100;
function bar() {
alert(a);
}
function foo() {
var a = 10;
bar();
}

foo(); -> 100

こうでないとおかしいです。100になる。なので、静的スコープとは関係ない話に。関係ないわけではないけど、グローバル変数を参照するという話です。グローバル変数ってスコープ関係なく参照できますもんね。普通。何が普通かって。


phpは動的スコープだけど、変わってる。

$a = 100;
function hoe()
{
echo 'a = '.$a;
}

hoe(); -> a =

とaの値は出力されません。

$a = 100;
function hoe()
{
global $a;
echo 'a = '.$a;
}

hoe(); -> a = 100;

phpのスコープも関数ですが、その外側の変数を参照しようとすると、globalキーワードを使用しなければなりません。
何回これにハマった事か…。
globalを使うコードを書かなきゃいけないアプリの書き方がいけないと思うんですけど…。

なんだかえらく長くなりました。
最初からperlで試してみていれば、レキシカルスコープって?って悩まなくて済んでいたような気がします。おしまい。
posted by ほえ at 17:40| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:


この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。