変数へのアクセスをフックする方法の比較 [言語比較]
Tcl
ビルトインの trace コマンドで変数がアクセスされたときに呼び出されるプロシージャを登録できる。今回比較した言語の中でもっとも真っ当かつ充実している。プロシージャ名を与える仕様なのでプロシージャを予め作る必要があり、フックしたい変数が1つしか無い場合などは後述の Ruby と比べるとやや面倒。
proc hook {name1 name2 op} {
if {$op == "write"} {
upvar #0 $name1 var
puts "$op $name1: $var"
} else {
puts "$op $name1"
}
}
trace add variable foo {read write} hook
set foo "hello world"
set bar $foo
実行結果は以下。
F:\>tclsh tryme.tcl write foo: hello world read foo
Ruby
組み込み関数の trace_var でグローバル変数への書き込みをフックできる。ブロックを使った書き方は簡潔でよい。ただしグローバル変数にしか使えず、書き込み時のみなのであまり面白い使い道はなさそう。ブロックに与えられる引数は変更後の値のみで変数名がない(変数名を使いたければ変数ごとに別々のブロックを書いてあげる必要がある)。
trace_var(:$foo) {|value|
print "write: #{value}"
}
$foo = "hello world"
Perl
tie を使えばできる。ある意味自由度は高いが面倒。フック関数側での変数名の使用は素直にやる方法はない。もっと工夫すれば何かやりようはあるのかも。
{
package trace;
sub TIESCALAR {
my ($pkg, $value) = @_;
bless \$value, $pkg;
}
sub FETCH {
my $self = shift;
print "read\n";
return $$self;
}
sub STORE {
my $self = shift;
my $value = shift;
print "write: $value\n";
$$self = $value
}
}
tie $foo, trace, $foo;
$foo = "hello world";
$bar = $foo;
print $bar;
Lua
グローバル変数テーブル _G を置き換えてしまってテーブルアクセスメタメソッドを書いてあげればどうにかできる。グローバル変数にしか使えない。そのままだとすべてのグローバル変数のアクセスで呼び出されてしまうので関数側で適宜条件分岐とかを書いてあげる必要がある。
local real_globals = {}
setmetatable(_G, {
__index = function(t, k)
if (k == "foo") then
print("read " .. k)
end
return real_globals[k]
end,
__newindex = function(t, k, v)
if (k == "foo") then
print("write " .. k .. ": " .. v)
end
real_globals[k] = v
end
})
foo = "hello world"
bar = foo
Python
わからない。
JavaScript
わからない。
Lisp
わからない。
コメント 0