SWI-PrologをC言語から叩く 

はてなブックマーク - SWI-PrologをC言語から叩く
Bookmark this on Delicious

7つの言語 7つの世界」の3つ目の言語がPrologなんですけど、触ってみると結構面白くて、とりあえずの感想としてはルールの表現力がハンパ無く高いなという事です。良く例で挙げられていますが、数独を解いたり、麻雀の手役を確認したりといった、一定のルールに基づいた処理を他の言語と比べてかなり楽に書けそうです。

これはちょっと習得しておきたいなと思うものの、上述のような特定の処理以外は他の言語と比べてあまりに貧弱です。今回使用した処理系の標準ライブラリはこれだけしかありません。

なので必要に応じてPrologを別の言語から使うという方が現実的なのかなということで、とりあえずPythonインターフェースを試したのですが、動かなかったので仕方なしにC言語から使ってみる事にしました。なお処理系はSWI-Prologというのを使う事にしました。(手元だとGNU Prologが日本語で文字化けを起こしました。)

まず以下のPrologのコードを見てください。ドラクエの道具屋(アリアハン)です。

% shop.pl
item(やくそう, 8).
item(どくけしそう, 10).
item(キメラのつばさ, 25).
item(おなべのフタ, 50).
 
available(G, X) :-
	item(X, G0),
	G0 =< G.

availableという述語を定義していて、持ち金(G)を指定してクエリを投げると、購入可能なアイテムが得られます。Prologからの実行は以下のようになります。

?- ['shop.pl'].
% shop.pl compiled 0.00 sec, 2,216 bytes
true.

?- available(10, X).
X = やくそう;
X = どくけしそう;
false.

?- available(100, X).
X = やくそう;
X = どくけしそう;
X = キメラのつばさ;
X = おなべのフタ.

これをC言語から使う訳ですが、公式ドキュメントによると以下のような手順になります。

  1. C言語から使用したいPrologの述語が記述されたplファイルを準備する(今回はshop.pl)
  2. Prologを叩くC言語のファイルを準備する(今回はmain.cpp)
  3. SWI-Prologにバンドルされているswipl-ldというツールでビルドして実行ファイルを作成

本来はPrologで使用する述語もC言語側から追加したいのですが、今回はまぁ良しとします。さて手順1のshop.plはもう準備できているので手順2から進める事にします。

手順2
C言語のコードを書きます。大した処理でもないのですが、まぁ情報が無いので苦労しました。処理の流れは、PL_initializeで初期化して、PL_Predicateでクエリに必要なpredicate_tオブジェクト(SQLみたいなイメージ)を構築します。作成したpredicateをPL_open_queryでPrologにクエリを投げて、PL_get_charsなんかで結果を取得するというものです。

#include <stdio.h>
#include <string.h>
#include <SWI-Prolog.h>

int main(int argc, char **argv) {
    if (argc == 1) {
        printf("Usage: shop [持ち金]\n");
        exit(0);
    }

    // initialize
    char* program = argv[0];
    char* plav[2] = { program, NULL };
    if (!PL_initialise(1, plav)) {
        PL_halt(1);
    }

    // build query
    int money = atoi(argv[1]);
    predicate_t pred = PL_predicate("available", 2, NULL);
    term_t h0 = PL_new_term_refs(2);
    term_t h1 = h0 + 1;
    PL_put_integer(h0, money);

    // exec query
    qid_t qid = PL_open_query(NULL, PL_Q_NORMAL, pred, h0);
    while (PL_next_solution((qid))) {
        char *s = NULL;
        PL_get_chars(h1, &s, CVT_ALL|REP_UTF8);
        printf("%s\n", s);
    }
    PL_close_query(qid);

    // cleanup
    PL_cleanup(1);

    return 0;
}

とにかく情報が無いのが一番困りました。唯一サンプルコードが見つかったのがコチラのページです。フランス語ですけど。少しハマったのがPL_get_charsで文字列を取得する際には上記のフラグを指定しないと文字列として取得できないという事です。あとPL_open_queryからPL_close_queryまでを一纏めにしたPL_call_predicateを使うと、何故か想定通りの挙動をしませんでした。

手順3
次にビルドします。これは簡単ですね。

$ swipl-ld -o shop main.cpp shop.pl
$ ls
main.cpp        shop            shop.pl

ところでswipl-ldは幾つかのビルド処理を一纏めにしているユーティリティです。内部で何が起こっているかは-vオプションを追加すると見れます。詳細は調べてませんがswiplに何やら怪しい処理をさせていますね。気が向いたらもう少し突っ込みたいところです。

$ swipl-ld -v -o shop main.cpp shop.pl
        eval `swipl --dump-runtime-variables`
                CC="gcc"
                PLBASE="/usr/lib/swipl-5.6.63"
                PLARCH="i386-darwin10.8.0"
                PLLIBS="-lpl"
                PLLIB="-lpl"
                PLCFLAGS="-no-cpp-precomp -pthread -fno-common"
                PLLDFLAGS="-O3 -pthread"
                PLSOEXT="dylib"
                PLTHREADS="yes"
        g++ -c -no-cpp-precomp -pthread -fno-common -D_REENTRANT -D__SWI_PROLOG__ -D__SWI_EMBEDDED__ -I/usr/lib/swipl-5.6.63/include -o main.o main.cpp
        g++ -o shop -O3 -pthread main.o -L/usr/lib/swipl-5.6.63/lib/i386-darwin10.8.0 -lpl -lpl
        swipl -f none -F none -g true -t "consult(['shop.pl']),qsave_program('pltmp-98271',[goal='\$welcome',toplevel=prolog,init_file=none])"
% shop.pl compiled 0.00 sec, 5,272 bytes
% halt
        cat pltmp-98271 >> shop
        chmod 755 shop
        rm main.o
        rm pltmp-98271

以上で実行ファイルshopが生成されました。早速実行してみます。ライセンスとかが出力されて不満ですが、まぁ最低限の挙動は示していますね。

$ ./shop 10
Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 5.6.63)
Copyright (c) 1990-2008 University of Amsterdam.
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
Please visit http://www.swi-prolog.org for details.

For help, use ?- help(Topic). or ?- apropos(Word).

やくそう
どくけしそう

以上、SWI-PrologをC言語から触ってみました。GNU Prologの場合は少しインターフェースが異なるようなので注意してください。まぁ正直Cから使えても仕方が無いのでPythonから使えない理由を少し探ってみたいと思います。Pythonの他に、Perlにもインターフェースが用意されているようですね。

関連する記事

  • 関連する記事はありません

タグ: , ,

コメントをどうぞ