SSブログ

変数へのアクセスをフックする方法の比較 [言語比較]

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

わからない。


nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。