リスト,配列,式の扱い方

Wolfram Symbolic Transfer Protocol (WSTP)を利用して,あらゆる型のデータを外部プログラムと交換することができる.非常に基本的な型については,WSTPテンプレートファイル中の:ArgumentTypes::ReturnType:を適当に設定すればよい.
Wolfram言語の仕様
Cの仕様
Integer
整数
int
Real
浮動小数点数
double
IntegerList
整数のリスト
int*,long
RealList
浮動小数点数のリスト
double*,long
String
文字列
char*
Symbol
シンボル名
char*
Manual
WSTPルーチンを直接呼び出す
void
基本的な型
整数のリストを引数に取るWSTPテンプレート:
:Begin:
:Function: h
:Pattern: h[a_List]
:Arguments: {a}
:ArgumentTypes: {IntegerList}
:ReturnType: Integer
:End:
テンプレートに対応するCのソースコード.リストの長さを示す引数alenが関数に渡されていることに注意する:
int h(int *a, long alen) {

int i, tot=0;

for(i=0; i<alen; i++)
tot += a[i];

return tot;
}
これは関数hの指定を含む外部プログラムをインストールする:
外部コードを呼び出す:
パターンh[a_List]にマッチしないので外部コードは呼び出されない:
パターンはマッチするが,リストの要素の外部コードの期待するタイプではない.そのため$Failedが返っている:
基本的な型は自由に組み合せて使うことができる.ただし,IntegerListRealListとを使う場合は,リストの長さを表す特別な引数をCのソースコード中に与えなければならない.
:ArgumentTypes:の指定:
:ArgumentTypes:  {IntegerList, RealList, Integer}
対応するCの関数の宣言はこのようになる:
void f(int *a, long alen, double *b, long blen, int c)
WSTPによってCプログラムに渡されたリストの最初の要素は,Wolfram言語にとって標準的な1番目ではなく,Cの標準である0番目として参照されることに注意する.
さらに,Cの規格に従い,Stringで示される文字列は,ヌルバイト0で終了するchar*で示されるオブジェクトとして渡される.特殊文字の取扱いについては「WSTPプログラムの移植性」で説明する.
WSPutInteger32(stdlink,int i)
整数を1個送信
WSPutReal64(stdlink,double x)
浮動小数点数を1個送信
WSPutInteger32List(stdlink,int*a,int n)
a から始まる n 個の整数のリストを送信
WSPutReal64List(stdlink,double*a,int n)
a から始まる n 個の浮動小数点数のリストを送信
WSPutInteger32Array(stdlink,int*a,int*dims,NULL,int d)
次元 dims,深さ d のリストとして配列を送信
WSPutReal64Array(stdlink,double*a,int*dims,NULL,int d)
浮動小数点数の配列を送信
WSPutString(stdlink,char*s)
文字列を送信
WSPutSymbol(stdlink,char*s)
シンボル名を送信
WSPutFunction(stdlink,char*s,int n)
頭部が s,引数 n 個の関数の送信を開始
Wolfram言語へデータを送信するWSTP関数
WSTPテンプレートファイルを使用する場合にmprepmccが実際に行うことは,WSTPライブラリ関数を明示的に呼び出すCプログラムを生成することである.そのCプログラムのコードを読めば,WSTPライブラリ関数を直接使用する方法の例が見られる.mccを使う場合,-gオプションを忘れないようにする.そうしないと生成されたソースコードは自動的に消去されてしまう.
もし,外部関数が整数か浮動小数点数を1つだけ返すようなものなら,WSTPテンプレートファイル中の:ReturnType:IntegerあるいはRealを与えるだけでよい.しかし,Cのメモリアロケーション/デアロケーションの仕方が原因して,:ReturnType:IntegerListRealListを与えることはできない.そのような構造体を返すようにしたいなら,WSTPライブラリ関数を明示的に呼び出すCプログラムを書く必要がある.この場合,:ReturnType:Manualを指定する.
整数の引数を1つ取り,明示的にWSTPを使って2進表現で桁数のリストを返すWSTP関数のテンプレート:
:Begin:
:Function: bits
:Pattern: bits[i_Integer]
:Arguments: {i}
:ArgumentTypes: {Integer}
:ReturnType: Manual
:End:
関数の返り型はvoidと宣言する:
void bits(int i) {

int a[32], k;
Cの配列aに値を設定する:
   for(k=0; k<32; k++) {
a[k] = i%2;
i >>= 1;
if (i==0) break;
}

if (k<32) k++;
配列a中のk個の要素をWolfram言語に戻す:
    WSPutInteger32List(stdlink, a, k);
return ;
}
外部関数bitsを持つプログラムをインストールする:
外部関数はビットのリストを返す:
Cの配列int a[n1][n2][n3]は,WSPutInteger32Array()を利用して,深さ3のリストとしてWolfram言語に送信することができる.
三次元Cの配列を宣言する:
   int a[8][16][100];
配列dimsを用意し,aの次元を保持するように初期化する:
   int dims[] = {8, 16, 100};
三次元配列aをWolfram言語に送信し,深さ3のリストを作成する:
    WSPutInteger32Array(stdlink, a, dims, NULL, 3);
いかなるWolfram言語の式も,WSTP関数で作ることができる.基本的に,作成したいWolfram言語式のFullForm(完全形)に直接相当するWSTP関数を順に呼び出せばよい.
2つの引数のWolfram言語関数Plusを設定する:
WSPutFunction(stdlink, "Plus", 2);
最初の引数に77を与える:
WSPutInteger32(stdlink, 77);
2番目の引数にシンボルxを与える:
WSPutSymbol(stdlink, "x");
一般に,まず,WSPutFunction()を呼び,作成したいWolfram言語関数の頭部とその関数が引数をいくつ取るかを与える.その後,もうひとつ,WSTP関数を呼んで関数の引数を順に設定する.Wolfram言語の式の一般的な構造と頭部の概念については「式」で解説している.
要素数が2のWolfram言語リストを生成する:
WSPutFunction(stdlink, "List", 2);
リストの最初の要素は10個の要素を持つCの配列r
WSPutInteger32List(stdlink, r, 10);
最初のリストの2番目の要素は要素数が2のリスト:
WSPutFunction(stdlink, "List", 2);
サブリストの最初の要素は浮動小数点数である:
WSPutReal64(stdlink, 4.5);
サブリストの2番目の要素は整数である:
WSPutInteger32(stdlink, 11);
WSPutInteger32Array()WSPutReal64Array()とを使えば,Cが前もって一次元的に割り付けたメモリ上の配列を送信することができる.しかし,Cプログラムの実行中に作った配列は,ネストされたポインタの集まりであることが普通である.そのような配列をWolfram言語に送信するには,WSPutFunction()を何度か呼び,最後にWSPutInteger32List()を呼べばよいだろう.
aを整数のリストのリストのリストとして宣言する:
int ***a;
n1要素のWolfram言語リストを作成する:
WSPutFunction(stdlink, "List", n1);
for (i=0; i<n1; i++) {
n2要素のサブリストを作成する:
    WSPutFunction(stdlink, "List", n2);
    for (j=0; j<n2; j++) {
整数リストを書き出す:
        WSPutInteger32List(stdlink, a[i][j], n3);
    }
}
WSTP関数を利用して作成した式はWolfram言語に送信された直後に評価されることに注意する.このことは,例えば,Wolfram言語に送信した配列を転置したいとき,配列を表す式をTransposeで囲むだけでよいことを意味する.すなわち,配列を表す式を生成する前に,WSPutFunction(stdlink,"Transpose",1);を呼び出すだけでよい.
Wolfram言語に送信したデータを後処理するというアイディアの使い道はたくさんある.1つの例は,長さを前もって知ることのできないリストを送信する場合である.
次々に要素を加えてWolfram言語のリストを作成する:
要素が次々にネストしたサブリストに入ったリストを作成する:
Flattenはリストのネストを外す:
Sequenceは自動的にネストを外す:
WSPutInteger32List()を呼び出すには,送信したいリストの長さを知る必要がある.しかし,ネストされたSequenceの列を作成することにすれば,リスト全体の長さを送信前に知る必要がなくなる.
結果をListで囲んでおく:
WSPutFunction(stdlink, "List", 1);
while( 条件 ) {
/* 要素を生成 */
次のレベルのSequenceを作成する:
    WSPutFunction(stdlink, "Sequence", 2);
要素を送信する:
    WSPutInteger32(stdlink,  i );
}
最後のSequenceオブジェクトを閉じる:
WSPutFunction(stdlink, "Sequence", 0);
WSGetInteger32(stdlink,int*i)
整数を取得し,アドレス i に格納する
WSGetReal64(stdlink,double*x)
浮動小数点数を取得し,アドレス x に格納する
Wolfram言語からデータを取得するための基本的な関数
WSTPは,外部プログラムからWolfram言語へデータを送信するWSPutInteger32()のような関数を提供する.また,WSTPにはWolfram言語のデータを外部プログラムに渡すためのWSGetInteger32()のような関数も用意されている.
WSTPテンプレート中の:ArgumentTypes:のリストはManualで終ることができ,これは他の引数があることを示す.その式はWSTP関数を呼び出して受け取る.
:Begin:
:Function: f
Wolfram言語の関数fは3つの引数を取る:
:Pattern:        f[i_Integer, x_Real, y_Real]
3つの引数はすべて外部関数に渡される:
:Arguments:      {i, x, y}
最初の引数だけが外部関数に直接送信される:
:ArgumentTypes:  {Integer, Manual}
:ReturnType:     Real
:End:
外部関数は1つの引数しか,明示的に取得しない:
double f(int i) {
変数xyを宣言する:
   double x, y;
WSGetReal64()を呼んでリンクからデータを明示的に取ってくる:
   WSGetReal64(stdlink, &x);
WSGetReal64(stdlink, &y);
   return i+x+y;
}
WSGetInteger32(link,pi)のようなWSTP関数は標準的なCのライブラリ関数fscanf(fp,"%d",pi)とほとんど同じように動作する.最初の引数はどのリンクからデータを取得するかを示し,最後の引数は受け取ったデータをどのアドレスに格納するかを示している.
WSCheckFunction(stdlink,"name",int*n)
関数の頭部をチェックし,その関数がいくつの引数を持っているかを保存する
WSTP経由で関数を取得する
:Begin:
:Function: f
Wolfram言語の関数fは整数のリストを引数に取る:
:Pattern:        f[a:{___Integer}]
リストは外部関数に直接渡される:
:Arguments:      {a}
引数は外部関数がマニュアルで取得する:
:ArgumentTypes:  {Manual}
:ReturnType:     Integer
:End:
外部関数は引数を明示的に取らない:
int f(void) {
局所変数の宣言:
    int n, i;
int a[MAX];
送信された関数がリストであることをチェックし,いくつ要素を持っているかをnに記録する:
    WSCheckFunction(stdlink, "List", &n);
リスト中のそれぞれの要素を受信し,a[i]に保存する:
   for (i=0; i<n; i++)
WSGetInteger32(stdlink, a+i);
ほとんどの場合,Wolfram言語側で外部プログラムに送るデータが期待通りの構造かどうかを確認することができる.WSCheckFunction()の戻り値は一般に,データが指定された名前の関数からなる場合にのみ,MLSUCCESSとなる.
ネストされたリストやその他のオブジェクトを受信することは,WSCheckFunction()を適切な順番で呼び出すことで可能である.
WSGetInteger32List(stdlink,int**a,int*n)
整数のリストを取得し,それを格納するに十分なメモリを割り付ける
WSGetReal64List(stdlink,double**a,int*n)
浮動小数点数のリストを取得する
WSReleaseInteger32List(stdlink,int*a,int n)
整数リストに関連したメモリを解放する
WSReleaseReal64List(stdlink,double*a,int n)
浮動小数点数リストに関連したメモリを解放する
数値のリストを取得する
外部関数がWolfram言語からデータを取得するとき,データを保管する場所を確保しなければならない.もし,そのデータが整数1個であったら,WSGetInteger32(stdlink,&n)のように,整数がintnの場所を使うように宣言するだけで十分である.
しかし,データが長さの分からない整数リストである場合,外部プログラムが実際に呼び出された時点でそのリストを保存するためのメモリを割り付けなければならない.
WSGetInteger32List(stdlink,&a,&n)は自動的にこのメモリ割付けを実行して,a をその割付けの結果を指すポインタとする.WSGetInteger32List()のような関数で割り付けられたメモリはすべて特殊な領域に配置される.そのメモリを修正したり,直接解放することはできない.
この外部関数は整数リストを受信する:
int f(void) {
局所変数の宣言.aは整数の配列:
    int n;
int *a;
整数リストを受信する.aを受信したデータの場所を指すようにする:
    WSGetInteger32List(stdlink, &a, &n);
整数リストを保管していたメモリを解放する:
    WSReleaseInteger32List(stdlink, a, n);
...
}
:ArgumentTypes:としてIntegerListを指定した場合,WSTPは外部関数が終了した直後,リスト用に使ったメモリを自動的に解放する.しかし,WSGetInteger32List()で明示的に整数リストを受け取った場合は,そのリストの利用が終ったら忘れずにメモリを解放することが重要である.
WSGetInteger32Array(stdlink,int**a,int**dims,char***heads,int*d)
あらゆる深さの整数配列を受信する
WSGetReal64Array(stdlink,double**a,int**dims,char***heads,int*d)
あらゆる深さの浮動小数点数配列を受信する
WSReleaseInteger32Array(stdlink,int*a,int*dims,char**heads,int d)
整数配列に関連するメモリを解放する
WSReleaseReal64Array(stdlink,double*a,int*dims,char**heads,int d)
浮動小数点数配列に関連するメモリを解放する
数値配列を受信する
WSGetInteger32List()はWolfram言語のリストから一次元の整数配列を抜き出す.WSGetInteger32Array()はあらゆる深さのリストやWolfram言語式から整数配列を抜き出す.
構造体にある,レベル i のWolfram言語関数の名前は,heads[i]に文字列として保存される.レベル i の構造体のサイズは dims[i]に,全体の深さは d に保存される.
複素数のリストを外部プログラムに渡すとき,WSGetReal64Array()は実部と虚部のペアからなる列を含む二次元配列を作るだろう.このとき,heads[0]"List"heads[1]"Complex"となるだろう.
Wolfram言語のIntegerDigitsRealDigitsを利用して数を数字のリストに変換すれば,外部プログラムとの間で数の任意精度を簡単に交換できる.
WSGetString(stdlink,char**s)
文字列を取得する
WSGetSymbol(stdlink,char**s)
文字列を取得する
WSReleaseString(stdlink,char*s)
文字列に関連したメモリを解放する
WSReleaseSymbol(stdlink,char*s)
シンボル名に関連したメモリを解放する
文字列とシンボル名の取得
:ArgumentTypes:Stringを指定すると,WSTPは外部関数が終了した直後,文字列用に使っていたメモリを自動的に解放する.その文字列を参照し続けたい場合には,メモリを割付け,文字列に含まれる文字のそれぞれを明示的にコピーする必要がある.
WSGetString()を利用する場合は,関数が終了しても文字列を保持するメモリは解放されない.そのため,文字列を参照し続けることが可能である.WSGetString()で返されたメモリに書き込むことで文字列の内容を修正しないように注意しなければならない.文字列が必要でなくなったら,WSReleaseString()を呼んで文字列に関連したメモリを解放しなければならない.
WSGetFunction(stdlink,char**s,int*n)
関数情報を取り込む(頭部名は s に,引数の数は n に入れられる)
WSReleaseSymbol(stdlink,char*s)
関数情報を保管しているメモリ領域を解放する
任意関数の取得と解放
外部プログラムにおいてどの関数型のオブジェクトを取り込むかがはっきりしている場合は,WSCheckFunction()を使えばよいので簡単である.しかし,関数型が判明しないときは,WSGetFunction()を使うしかない.後者を使った場合は終了時にWSReleaseSymbol()を呼び出して,WSGetFunction()で見付けた関数頭部を保管するのに使ったメモリを解放する必要がある.