Matlabでポインタ(間接参照)を実現する
私は、このブログではPythonやRubyのことばかり書いていますが、仕事ではMatlabという数値計算ソフトを使っています。
このMatlabというソフトは、ツールとしては非常に便利ですが、ことプログラミング言語として見ると、非常に大きな欠陥があります。
それは、間接参照、C言語でいうポインタに相当するものが無いということです。
Python・Ruby・Javaなどの言語では、プリミティブ型を除くと、変数は全てオブジェクトへの参照です。
そのため、関数やメソッド内で変数の状態を操作した場合、呼び出し側の変数も状態が更新されます。
# Pythonの例 def change_array(a, value): a[0] = value a = [1] assert(a[0] == 1) change_array(a, 2) assert(a[0] == 2) print 'OK'
しかし、Matlabの場合、変数は値なので、関数内で変数を操作しても呼び出し側の変数は更新されません。
function change_array_by_func() a = [1]; assert(a(1) == 1, 'a(1) == 1'); change_array(a, 2); assert(a(1) == 2, 'a(1) == 2'); % ここでエラー disp('OK'); end function change_array(a, value) a(1) = value; end
一応、Matlabでもhandleというクラスから派生したクラスのオブジェクトは参照型となるらしいですが、その方法の場合、いちいち新しいクラスを作らなければいけないので非常に不便です。
また、比較的新しいバージョンのMatlabでないと、その機能すら使えないようです。
ここで、以前読んだLET OVER LAMBDAという本に書いてある、クロージャでポインタを模倣するテクニックを思い出しました。
LET OVER LAMBDAに書いてある方法とは少し違いますが、以下のプログラムでMatlabで間接参照を実現することが出来ます。
function self = Pointer(pointee) pointer = pointee; self = struct; function retval = get() retval = pointer; end function retval = set(new_pointee) pointer = new_pointee; retval = self; end self.get = @get; self.set = @set; end
次にPointerのデモプログラムを示します。
function test_Pointer() p1 = Pointer(1); p2 = p1; assert(p1.get() == 1, 'p1.get() == 1'); assert(p2.get() == 1, 'p2.get() == 1'); change_pointer(p1, 2); assert(p1.get() == 2, 'p1.get() == 2'); assert(p2.get() == 2, 'p2.get() == 2'); disp('OK'); end function change_pointer(p, value) p.set(value); end
関数内で行った変更が呼び出し元でも反映されており、なおかつp1のコピーであるp2にも変更が反映されています。
ちなみに、先ほども紹介したLET OVER LAMBDAという本はLispのクロージャとマクロについて書かれた本です。
プログラミングの書籍としては難解な部類に入ると思いますが、第1章のクロージャの部分だけでも十分元がとれるほどの内容です。