Prolog のリストを作る

整数 int の配列 body[] があって、その要素を Prolog のリストに変換する例を取り上げる。 最終的にリストは、アトムや整数と同様に SP_term_ref 型の変数で参照される。

    SP_term_ref list = SP_new_term_ref();
配列の長さが int len に入っているものとすると、
    SP_term_ref elm;
    for (i=0; i<len; i++) {
        elm = SP_new_term_ref();
        SP_put_integer(elm, body[i]);
        SP_cons_list(list, elm, list);
    }
list に格納できる。
    int SP_cons_list(SP_term_ref t,
                     SP_term_ref head,
                     SP_term_ref tail)
は、リストのヘッド (car) が head で、 テイル (cdr) が tail であるようなリストを作って、 t に入れる関数である(t = [head|tail])。 つまり、この関数ではリストの先頭に要素を加えていくことになる。上記の例でも、
    int body[] = {1,2,3,4,5}
という配列から作られるリストは、
    [5,4,3,2,1]
になる。ちょっと嫌だけど、Prolog で reverse/2 すれば、問題無し。

注意

SP_cons_list() によって作成されたリストを引数に持って Prolog の述語をコールすると、Prolog の global stack に残されてしまう。 Prolog 側でバックトラックする際に必要になる場合もあるから、という理由だが、バシバシにリストを作成するようなプログラムだと、Prolog に割り充てられるメモリが無くなって Segmentation fault で落ちる。

これを回避するには、ちょっと裏技っぽいが repeat/0 を使う。この述語は、もともと、

predA,
repeat,
generate(Datum),
action(Datum),
test(Datum),
!,
predB,
のように使う。 test(Datum) で失敗した場合、predA までバックトラックがかかるものとすると、その際、repeat に差し掛かったところで、test(Datum) までに消費したメモリ領域を即座に回収してくれる。 この副作用を利用して、リストが不要になる時を見計らって repeat/0 をコールすれば、前回 repeat/0 をコールした時点のスタック状態まで戻してくれる。