ボンジュール・マドモアゼル

本サイトの情報は自己責任にてご利用下さい。

[Prolog] SWI-Prolog − 属性付き変数 (Attributed variables)

 
SWI-Prolog の属性付き変数のまとめ 以下、SWI-Prolog 5.11.15 Reference Manual の部分翻訳(非公式)。
誤訳があるかもしれない。色付き文字は訳者の補足。

6.1 Attributed variables
6.1 属性付き変数

属性付き変数は、Prologのユニフィケーション・アルゴリズムを拡張する技法(Christian Holzbaur 1992 フックを使った属性付き変数の束縛)を提供する。Prolog コミュニティにおいて、属性付き変数の定義とインターフェースの形式については合意がない。SWI-Prologのインターフェースは Bart Demoen 2002 によって hProlog Demoen 向けに実現されたものと同じである。このインターフェースはシンプルで、Leuven CHR システムが走るすべての Prolog システムで利用できる。(section 7 と Leuven CHR のページを見よ)
属性付き変数の束縛が起こると、フックを起動するゴールが最初の可能な機会に実行される。現実装では、フックは、属性付き変数が、節の頭部の一部と単一化した直後か、外部のネイティブ関数(foreign language predicate)が成功した後に実行される。
A = 1 という単一化も =(A,1) というゴールが起動され、節 =(X,X):- true. の頭部との単一化を試みることに注意。

それぞれの属性は、モジュールに関連付けられ、そのモジュールの中でフック (attr_unify_hook/2) が実行される。
下の例では、完全ではないが、簡単な有限ドメインの推論を実現している。
:- module(domain,
          [ domain/2                    % Var, ?Domain
          ]).
:- use_module(library(ordsets)).

domain(X, Dom) :-
        var(Dom), !,
        get_attr(X, domain, Dom).
domain(X, List) :-
        list_to_ord_set(List, Domain),
        put_attr(Y, domain, Domain),
        X = Y.

%       An attributed variable with attribute value Domain has been
%       assigned the value Y

attr_unify_hook(Domain, Y) :-
        (   get_attr(Y, domain, Dom2)
        ->  ord_intersection(Domain, Dom2, NewDomain),
            (   NewDomain == []
            ->  fail
            ;   NewDomain = [Value]
            ->  Y = Value
            ;   put_attr(Y, domain, NewDomain)
            )
        ;   var(Y)
        ->  put_attr( Y, domain, Domain )
        ;   ord_memberchk(Y, Domain)
        ).

%       Translate attributes from this module to residual goals

attribute_goals(X) -->
        { get_attr(X, domain, List) },
        [domain(X, List)].

このコードに対する質問とその答え:
?- domain(X, [a,b]), X = c               ==> fail
?- domain(X, [a,b]), domain(X, [a,c]).   ==> X = a
?- domain(X, [a,b,c]), domain(X, [a,c]). ==> domain(X, [a, c])

述語 domain/2 は、(1番目の節に)フェッチするか、(2番目の節で)変数に属性 domain(単一化されることが可能な値の集合)を割り当てる。
2番目の節では、X がすでに属性 domain を持つという可能性に対処するために、まず未束縛の変数 Y に属性を関連付け、その後に Y を X に単一化する。
述語 attr_unify_hook/2 は、フックであり、属性 domain 付き変数に値が割り当てられた直後に呼ばれる。
変数が具体的な値に束縛された簡単なケースでは、その値がドメインにあるかどうかをチェックする。
他方、ドメインの積集合を取るか、積集合が空であれば、失敗する(1番目の例)、
積集合がひとつの値のみある場合は、簡単に値を変数の割り当てる(2番目の例)、
そうでなければ、新しいドメインを変数に割り当てる(3番目の例)。
非終端 attribute_goals/3 は、現在保持されている属性をユーザが読み取り可能なゴール(呼び出されるとき、属性を使えるようにする)の形式に変換するのに使われる。

6.1.1 Attribute manipulation predicates
6.1.1 属性操作述語
attvar( @Term)
項が属性付き変数であれば、成功する。var/1 は、属性付き変数についても成功することに注意せよ。属性付き変数は、put_attr/3 によって作成される。
put_attr(+Var, +Module, +Value)
Var が変数か属性付き変数であれば、Module の名前を持つ属性に Value を設定する。この名前を持つ属性が既に Var に関連付けられている場合、古い値は置き換えられる。バックトラックは古い値を復元する(つまり. 属性は変更可能な項である。setarg/3を参照のこと)。
この述語は、Varが変数でない場合、表記エラーを起こし、Moduleがアトムでない場合、型エラーを起こす。
get_attr(+Var, +Module, -Value)
Moduleの名前を持つ属性の現在の値を要求する。Varが属性付き変数でないか、Moduleで名付けられた属性が Var に関連付けられていない場合、この変数は、静かに失敗する。Moduleがアトムでない場合、型エラーを起こす。
del_attr(+Var, +Module)
Moduleの名前を持つ属性を削除する。もし、Var が最後の属性を失うと、その変数は伝統的なPrologの変数に戻される。Moduleがアトムでない場合、型エラーを起こす。その他のケースでは、その名前の属性が存在するか否かにかかわらず、この述語は成功する。

6.1.2 Attributed variable hooks
6.1.2 属性付き変数のフック

属性名はモジュールに結び付けられる。これは、属性付き変数に関する特定の操作が、属性名にマッチするモジュールのフックを起動することを意味する。
attr_unify_hook(+AttValue, +VarValue)
フックは、属性付き変数が記述されるそのモジュールに定義されなければならない。この述語は、非変数項か、他の属性付き変数と単一化された後に呼び出される。AttValue は変数に関連付けらていたこのモジュールにおける属性であり、VarValueは変数の新しい値である。この述語は、通常、変数とVarValueとの束縛を拒否するために失敗し、束縛を取り消すため、バックトラックを強制させる。もし VarValue が他の属性付き変数の場合、フックは、しばしばそれらふたつの属性値を結合し、結合された属性値を put_attr/3 を使って、改めてVarValue に関連付ける。
attr_portray_hook(+AttValue, +Var)
write_term/2 およびこの類の述語において、オプション attributes(portray) が有効な場合、各属性に対して呼び出される。このフックが成功するなら、属性は、意図された印刷が行われる。そうでない場合、 Module = ... が、変数の存在を示すために印刷される。属性値のやり取りを取り扱う新しい基盤は、 copy_term/3 とそのフック attribute_goals/3 に基づく必要がある。
attribute_goals(+Var)
この非終端述語は、モジュールで定義されるならば、そのモジュールの属性を、属性を残存するゴール (residual goal) として映し出すため copy_term/3 によって使われる。この述語は、また、質問を実行した後、属性を残存するゴールを得るためにtoplevel によって使われる。

6.1.3 Operations on terms with attributed variables
6.1.3 属性付き変数を持つ項の操作

copy_term(+Term, -Copy, -Gs)
Termの属性を持たないコピーとして標準的な項 Copyと、属性を表現するゴールのリストGs を作成する。ゴール maplist(call,Gs) は、Copy の属性を再形成する。その属性に対応するモジュールで定義された非終端 attribute_goals/3 は、属性値をゴールのリストに変換するのに使われる。この構成要素は、移植可能な分かりやすいやり方で、保留された属性をレポートするために toplevel によって使われる。この述語は、制約による項の通信および推論にとって推奨される方法である。
例:foo.pl
:- module(foo,[]).

attribute_goals(X) -->
        { get_attr(X, foo, Value) },
        [fofo(X, Value)].

?- put_attr(X,foo,v1),put_attr(Y,bar,v2),copy_term(a(X,Y),Copy,Gs),del_attrs(X),del_attrs(Y).
Copy = a(_G1107, _G1108),
Gs = [fofo(_G1107, v1), put_attr(_G1108, bar, v2)].

copy_term_nat(+Term, -Copy)
copy_term/2 のように使う。ただし、属性はコピーされず、新しい未処理の変数に置き換えられる。
term_attvars(+Term, -AttVars)
AttVars は、Termに含まれるすべての属性付き変数のリストである。term_attvars/2 は再帰的に動作する。この述語は、循環安全 Cycle-safe である。ゴール term_attvars(Term,[]) は、項 Term が属性を持っていないことを効率的なテストである。つまり、最初の属性付き変数が見つかった時点でスキャンが中断される。

6.1.4 Special purpose predicates for attributes
6.1.4 属性に関する特殊用途の述語

通常のユーザコードは、put_attr/3, get_attr/3 および del_attr/2 で処理される。
この節のルーチンは、変数の全属性のリストをフェッチおよび設定する。
これらの述語の使用は、印刷に限られたものか、ほかの特殊な目的の操作が想定される。
get_attrs(+Var, -Attributes)
Var のすべての属性を取得する。属性は、att(Module, Value, MoreAttributes) の形式の項である。MoreAttributes は、最後に設定された属性を示す。
例:
?- put_attr(X,foo,a),put_attr(X,bar,b),get_attrs(X,Y),del_attrs(X).
Y = att(foo, a, att(bar, b, [])).

put_attrs(+Var, -Attributes)
すべての属性を設定する。Attributes の説明については get_attrs/2 を参照せよ。
del_attrs(+Var)
Var が属性付き変数の場合、すべての属性を削除する。そうでない場合、副作用なしに成功する。

[Prolog] SWI-Prolog − モジュール

 
SWI-Prolog のモジュール機能について

SWI-Prolog におけるモジュールの構成を下図に示す。
SWI-Prolog モジュール構成
SWI-Prolog は、system と user のふたつの予約済みモジュールがある。
module/2 ディレクティブの指定がないファイルを consult した場合、user モジュールに述語が定義される。

以下、SWI-Prolog 5.10.2 Reference Manual の翻訳
誤訳があるかもしれない。色付き文字は訳者の補足。

5.10 Reserved Modules and using the `user' module
5.10 予約済みモジュールと user モジュールの使用

SWI-Prolog には2つの特別なモジュールが用意されている。ひとつめは、system モジュールである。このモジュールには、すべての組込み述語が含まれる。system モジュールは、インポートモジュールを持たない。ふたつ目の特別なモジュールは、user モジュールである。このモジュールは、ユーザの最初の作業空間を形成する。初期の作業空間には何もない。 userモジュールは、system モジュールをインポートしており、すべての組込み述語を利用できるようにしている。

他のすべてのモジュールは、user モジュールからインポートを行う。これは、それらのモジュールが、明示的にそれらをインポートせずとも user モジュールに対してインポートされたすべての述語が使えることを意味している。もしアプリケーションが、use_module/1を使ってuserモジュールからすべてのモジュールをロードする場合、特別な注意なしで、すべてのモジュールが、すべてのエクスポートされた述語にアクセスできる C言語と同様なスコープシステムを実現する。



5.2 Defining a Module
5.2 モジュールの定義

モジュールは、通常、モジュールファイルをロードすることで作成される。モジュールファイルは、 module/2 ディレクティブが最初の項として指定されているファイルのことである。 module/2 ディレクティブは、モジュールの名前と外部に公開する述語を宣言する。ファイルの残りの内容は、そのモジュールにロードされる。
以下は、モジュールファイルの例である。述語 reverse/2 と隠されたヘルパー述語 rev/3 を定義している。モジュールはすべての組込み述語を使うことができ、デフォルトでは、システム述語を再定義できない。redefine_system_predicate ディレクティブを使用することでシステム述語を再定義することが可能となる。
:- module(reverse, [reverse/2]).

reverse(List1, List2) :-
        rev(List1, [], List2).

rev([], List, List).
rev([Head|List1], List2, List3) :-
        rev(List1, [Head|List2], List3).

このモジュールの名前は、reverse である。典型的に、モジュール名は、拡張子を除いたファイル名と同じ名前で定義される。しかし、この命名規則は強制されるものではない。モジュールはフラットな唯一の名前空間に配置される。そのため、モジュール名は、衝突を避けるよう注意して選ばなければならない。
我々が見えるように、モジュールシステムの典型的アプリケーションは、ソーステキストで明示的にモジュールの名前をめったに使用しない。
:- module(+Module, +PublicList)
このディレクティブは、ソースファイルの最初の項でしか使うことは出来ない。これは、ファイルがモジュールファイルであることを宣言し、モジュール名を Module とし、PublicList にリストされた述語をエクスポートする。
PublicList は述語指示子 predicate indicator (name/arityname//arity)か、または op(Precedence, Type, Name) を使った演算子の宣言のリストである。
エクスポートリストで定義された演算子は、このモジュールをインポートしたモジュールと同様に、モジュールのなかで利用できる。section 4.23 も参照せよ。
(Ciao Prolog と互換)、もし、Module が束縛されていなければ、ロードされているファイルの拡張子を省いた basename で単一化される。例えば foo.pl に :- module(_, [bar/0]). と宣言した場合、モジュール foo が作成される。

5.4 Defining a meta-predicate
5.4 メタ述語の定義

メタ述語は、動的に他の述語を呼び出したり、述語を修正したり、述語の属性について推論などを行う述語である。メタ述語は、操作対象となる述語を記述するために、複合項か述部指示子(predicate indicator)を使用する。例:assert(name(jan)) abolish(name/1).
モジュールを導入すると、name+arity の組み合わせだけでは述語を特定することができなくなる。
これは、<module>:<term> という形式でモジュール名を修飾することで解決される。例:assert(person:name(jan)) abolish(person:name/1).
もちろん、我々は、assert/1 をモジュール内から呼び出すときには、述語をそのモジュールにローカルに assert することを期待する。言い換えれば、手作業による :/2の指定を避けたいのである。
meta_predicate/1 ディレクティブは、いくつかの引数が、述語を探索するのに使われる項であり、<module>:<term> のようにモジュール名が修飾されていない場合、モジュール名を補う必要があることをコンパイラに告げる。
下の例では、モジュール内で maplist/3 を定義するのに、このディレクティブを使っている。meta_predicate 宣言の引数 '2' は、この引数が、モジュール・センシティブであり、述語を呼び出すのに maplist/3 に渡されたもうふたつの項が使われるということを意味している。

コンパイラは、0..9 と : のみをモジュール・センシティブとして認識する。+ から ? までは、モードを示す。0..9 の値は、クロスリファレンスとシンタックス・ハイライティングで使用される。
ヘルパー述語 maplist_/3 には、meta-predicate 宣言が不要であることに注意せよ。なぜなら、maplist/3 が <module>:Goal としてモジュール名を修飾したものを、maplist_/3 に渡すからである。meta_predicate/1 の詳細を見よ。
:- module(maplist, [maplist/3]).
:- meta_predicate maplist(2, ?, ?).

%%      maplist(:Goal, +List1, ?List2)
%
%       True if Goal can successfully be applied to all
%       successive pairs of elements from List1 and List2.

maplist(Goal, L1, L2) :-
        maplist_(L1, L2, G).

maplist_([], [], _).
maplist_([H0|T0], [H|T], Goal) :-
        call(Goal, H0, H),
        maplist_(T0, T, Goal).
meta_predicate +Head, ...
カンマ区切りのリスト Head によって参照される述語をメタ述語として定義する。各頭部の各引数は、メタ引数指定子とする。指定子は以下のとおりに定義される。0..9 と : のみが解釈され、モード宣言 +, -, ? は無視される。

0..9
引数は、この引数の後に続くN 個の引数に与えられた項と一体で述語の参照を使用する項である。例:call(0) , maplist(1, +).

:
引数は、モジュール・センシティブであるが、直接的に述語を呼び出さない。例:consult(:).

-
引数は、モジュール・センシティブではない。かつ、入力において束縛されていない。

?
引数は、モジュール・センシティブではない。かつ、モード指定はない。

+
引数は、モジュール・センシティブではない。かつ、入力において束縛されている(nonvar など)。

モジュール・センシティブな各引数(0..9 か : でマークされたもの)は、モジュール名が修飾されていなければ、呼び出し側のコンテキストのモジュール名が修飾される。<atom> がモジュール名と(:/2ではない)項を示しているアトムであるところでは、実装は、引数が <module>:<term> で渡されることを保証する。
以下は、簡単な宣言といくつかの質問である。
:- meta_predicate
        meta(0, +).

meta(Module:Term, _Arg) :-
        format('Module=~w, Term = ~q~n', [Module, Term]).
?- meta(test, x).
Module=user, Term = test
?- meta(m1:test, x).
Module=m1, Term = test
?- m2:meta(test, x).
Module=m2, Term = test
?- m1:meta(m2:test, x).
Module=m2, Term = test
?- meta(m1:m2:test, x).
Module=m2, Term = test
?- meta(m1:42:test, x).
Module=42, Term = test

meta_predicate/1 宣言は、メタ述語を定義するための移植可能な機構であり、旧いSWI-Prologで提供された廃止予定の述語 module_transparent/1, context_module/1, strip_module/3 に取って代わられるものである。

[Prolog] SWI-Prolog − 4.1 Notation of Predicate Descriptions

 

引数モードについて
SWI-Prolog 5.10.2 Reference Manual の
4.1 Notation of Predicate Descriptions を翻訳。
誤訳があるかもしれない。色付き文字は訳者の補足。

4.1 述語説明の表記法

述語の説明を、明白かつ簡潔に記すよう努める。まず、述語名はボールド体で記され、後にイタリック体で記された引数が続く。引数の名前の先頭には、mode indicator が付与される。 mode indicator の表記については、Prologのコミュニティにおいて完全な合意がない。我々は、以下の定義に従う。
+
引数は、要求された型を満たす項として具体化されている必要がある。このモードは、入力用の引数を意図している。@モードとの違いは、+モードでは、引数が変数を含む複合項の場合、述語の呼び出しによって、その変数が具体化されることが起りうる。下の例では、述語 retract(+Term) が変数を含む引数で呼び出され、変数が具体化されている。

retract(father(X,Y)).
X = john,
Y = taro.


-
引数は、束縛されていてはならない。型が適合する変数項でなければならない。このモードは、出力用の引数を意図している。

?
引数は、示された型の部分項に束縛されなければならない。変数は、任意の型に適合する部分項となることに注意せよ。このモードは、入力か出力、または、入力かつ出力用を意図している。
例)stream_property(S, reposition(Bool)). 項の reposition の部分は、入力であり、代入されていない Bool は出力である。

:
引数は、メタ引数である。+ を含意する。モジュール操作の詳細については、section 5 を見よ。

@
引数は、述語の呼び出しで(部分的にも)更なる代入が行われることはない。典型的な用途は、type-tests である。

!
引数は、変更可能な構造を含む。それは、setarg/3 か nb_setarg/3 を使い、変更されるだろう。
実行テキストの中で述語を言及するには、述語指示子 (predicate indicator)を使う。述語指示子の正規で最も一般的な形式は、<module>:<name>/<arity> という項である。モジュール名は、モジュールと無関係(組込み述語)か、コンテキストから推測可能な場合、しばしば、省略される。
DCG(section 4.11参照)に関するISO標準草案に準拠するSWI-Prolog は、[<module>]:<name>//<arity> という形で文法規則を言及することも許す。
すべての負でない整数 arity については、<name>//<arity><name>/<arity+2> と同じである。

参照される述語が定義されるかどうか、文法規則として使えるかどうかに関わらず、
//記法は、伝統的に述語指示子の指定が許されるすべての箇所で使うことができる。例:spy/1, dynamic/1.

翻訳ここまで

上述のとおり、SWI-Prolog において述語指示子 (predicate indicator) は、[<module>]:<name>//<arity> という形でも書ける。
<name>//<arity - 2><name>/<arity> と同じである。
ただし arity - 2 がマイナスになる場合は、<name>//(<arity - 2>) と括弧を付加する必要がある。



?- spy(foo/0).



?- spy(foo//(-2)).

という形でも書ける。

以下は、述語指示子 (predicate indicator) 関する SWI-Prolog 5.10.2 の実装。
算術演算子として判定されているのが面白い。


pl-proc.c
int
get_functor(term_t descr, functor_t *fdef, Module *m, term_t h, int how)
{ GET_LD
  term_t head = PL_new_term_ref();
  int dcgpi=FALSE;

  PL_strip_module(descr, m, head);

  if ( PL_is_functor(head, FUNCTOR_divide2) ||
       関数子が '/' 除算かどうか
       (dcgpi=PL_is_functor(head, FUNCTOR_gdiv2)) )
        関数子が '//' 端数処理付き除算かどうか
  { term_t a = PL_new_term_ref();
    atom_t name;
    int arity = 0;

    _PL_get_arg(1, head, a);
    if ( !PL_get_atom_ex(a, &name) )
      fail;
    _PL_get_arg(2, head, a);
    if ( !get_arity(a,
        (dcgpi ? 2 : 0),
        dcgpi フラグが立っているとき、arity に加える数として
        引数 extra に 2 を渡す
        (how&GF_PROCEDURE) ? MAXARITY : -1,
        &arity ) )
      fail;
[...]

pl-proc.c
static int
get_arity(term_t t, int extra, int maxarity, int *arity)
{ int a;

  if ( !PL_get_integer_ex(t, &a) )
    fail;
  if ( a < 0 )
    return PL_error(NULL, 0, NULL, ERR_DOMAIN,
		    ATOM_not_less_than_zero, t);
  a += extra;   引数の数を加算する

  if ( maxarity >= 0 && a > maxarity )
  { char buf[100];

    return PL_error(NULL, 0,
		    tostr(buf, "limit is %d, request = %d",
			  maxarity, a),
		    ERR_REPRESENTATION, ATOM_max_arity);
  }

  *arity = a;

  return TRUE;
}

[Prolog] SWI Prolog Editor

 
SWI Prolog Editor 4.07

フリーカーソルモード(free cursor mode)をオフにする
  1. Window → Configuration メニューを開く。
  2. Editor タブに切り替える。
  3. Cursor right of the end of line allowed のチェックを外す。
SWIEditorConfig

SWI-Prolog-Editor は、SWI-Prologの公式サイトの左側メニューからIDEを叩くと SWI-Prolog-Editor からダウンロードできる。
http://www.swi-prolog.org/
SWI-Prolog-Editor のダウンロードサイトへのリンク

[Prolog] Prolog メモ

 
Prolog: 空リスト [] は、atom である。
atom([]).
は 真.

\+ は、Prologの組み込み述語で、対象の述語を否定する。
例)\+father(terach,isaac).

lose(List1, List2) :-

next(List1, List2, X),
not(tick(List1, [X|List2], _)),
!.

http://d.hatena.ne.jp/koichik/20050816


Prolog君が、 List1 について
次にさせる手がないということ

これ凄いぞ。2ch PrologでまったりPart2 より
?- see('foo.pro'),repeat,read(X),(X=end_of_file;write_formatted('%t.n',[X]),fail),seen.
ファイルを最後まで読んでそのまま表示する簡単なもの。


24 :デフォルトの名無しさん[sage]:2010/11/10(水) 20:25:49
一応参考まで
http://www.google.com/codesearch
lang:prolog ^(is_?)?list([

25 :デフォルトの名無しさん[sage]:2010/11/10(水) 21:13:19
完全な定義は、
list(V) :- var(V),!,fail.
list([]).
list([H|T]) :- list(T).
次のページ