Matlabでポインタ(間接参照)を実現する

私は、このブログではPythonRubyのことばかり書いていますが、仕事ではMatlabという数値計算ソフトを使っています。
このMatlabというソフトは、ツールとしては非常に便利ですが、ことプログラミング言語として見ると、非常に大きな欠陥があります。
それは、間接参照、C言語でいうポインタに相当するものが無いということです。

PythonRubyJavaなどの言語では、プリミティブ型を除くと、変数は全てオブジェクトへの参照です。
そのため、関数やメソッド内で変数の状態を操作した場合、呼び出し側の変数も状態が更新されます。

# 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章のクロージャの部分だけでも十分元がとれるほどの内容です。

追記 (2012年2月11日)

MatlabJavaのライブラリを呼び出すことができますが、Javaのオブジェクトは参照型なので、例えばArrayListを使って間接参照を実現することが出来ます。こちらの方法のほうが上記の方法よりもおそらく効率もよいでしょう。ただし、関数ハンドラなどMatlabの一部のデータ型は、Javaのコレクション型に入れることができないようです。