AZ-PrologでHPSGなどの単一化文法を容易に実装するため、データ型として素性構造型を導入しました。
ICOTで開発されたCU-Prologを参考に仕様検討、動作検証をおこないましたが、次の点で異なります。
・セミコロンによる制約記述は取り入れておりません。
・変数に制約条件を直接記述するシンタックスを取り入れました。変数間の関係、チェックが簡素化できます。
・差分リストに似た素性構造の差分表現を導入しました。
型付素性構造は今後実装の予定です。
<素性構造>を取り扱うモードのとき(default) 中括弧で括られた任意の個数の<素性と値の対>のみを要素とする並びを<素性構造>とする。
空を除き、<素性と値の対>を一つも要素としない場合、従来構造として扱う。混在はエラーである。
<例>
{} 空の素性構造 { 氏名:山田太郎,生年月日:{年:1951,月:5,日:26},子供:[花子,一郎,二郎] ,趣味:X } { 品詞:動詞 |Else } 差分表現
素性構造でない従来型の例
{write(X),nl}
エラーとなる例
{ a:b,a:z } 同一素性が含まれている { a:b,x:z,A } <素性と値の対>と素性以外の項とが混在している { a:b,Z:z } 素性がアトムでない
<素性と値の対>
・'素性(アトム)' + '区切り記号(オペレータ)' + 素性値 で表現する。
<素性>
・素性は複合項のファンクタがアトムであることを必須とするのと同様に、アトムでなくてはならない。。
・素性は素性構造の同一レベルではユニークでなければならない。
・素性値が素性構造をとり、階層が異なる場合は親側と同一の素性があってもよい。
<素性値>
・素性値は素性構造を含む任意の項である。
<区切り記号>
・区切り記号はデフォルトでは,':'であるが、変更することができる。(下記:fs_delimiter/2)
区切り記号を変更し、'/'とすれば、Cu-Prolog,CILなどのICOT標準表記となる。
ただし、区切り記号 '/ ' はyfxなので、a/b/c などでは/(/(a,b),c)となり素性名がアトムにならないので注意。
また、op述語で '/ ' をxfyに変更してしまうと、数値計算に支障をきたすので注意。
<DCG補強項などとの混在>
<素性と値の対>を一つも含まない中括弧の構造は、従来と同様に扱われることによりDCGの補強項も問題なく混在可能である。
is_adult(A,{level:child}):- A <18 ,!. is_adult(A,{level:adult}). a(X) --> { X={age:A#is_adult(A,X)} },b(X). b(X) --> { write(end),nl },[end]. 展開: a(X,S,T) :- freeze(A,is_adult(A,X)),X={age:A}, b(X,S,T). 展開: b(X,[end|T],T):-write(end),nl. |?-X = {age:30},a(X,[end],[]). end X = {age:30,level:adult} yes
I1,I2とも、0,1,2のいずれかの整数
I1 現在の素性構造モード
I2 設定する素性構造モード
0: 素性構造型を利用しない。(従来と同じ。V8までとの互換性のために用意されている)
1: 素性構造型を利用する。
2: 素性構造型を利用する。デバッグモード
初期値は 1 に設定されている。
<注意>
本述語は、素性構造型を利用せず、かつ素性構造型に抵触する書式の従来プログラムがある場合に
従来シンタックスを適用するために用意されている。
<例>
:- fs_mode(_,0).
A1 : 現在の素性名と素性値の組を区切る中置きオペレータ名がユニファイされる。
A2 : 設定する新しい素性名と素性値の組を区切る中置きオペレータ名
中置きオペレータでない場合は同時にオペレータ宣言をしておく。
初期値は ":" 結合値=550,xfy。
<注意>
本述語は、CU-Prologのソースを利用するなど特別に必要な場合、そのソースファイルの先頭に切り替えを宣言する。その際、ユーザーの作成するプログラム中に現れる区切り記号もこれに統一しておかねばならない。
動的、頻繁に切り替えるのは混乱と誤動作をまねくので慎むべきである。
<例>
:- fs_delimiter(_,!). :- op(550,xfy,!). a({a!b,c!d}).
Term : 調べたい項
Termが素性構造型であるときのみ成功する。
<例>
|?- X={a:{b:bb,c:cc}},fstructure(X). yes
「素性構造を取り扱うモード」(デフォルト)と 「素性構造を取り扱わないモード」では中括弧で括られたデータ同士のユニフィケーションの振る舞いが異なるので注意。
・素性構造は素性構造または変数とのみユニフィケーションする。他の型とでは失敗する
・素性構造1と素性構造2のユニフィケーションにおいて、
(1)1に含まれる素性が2の同一階層に含まれるとき、その素性値同士を再帰的にユニフィケーションする。
(2)1に含まれる素性が2の同一階層に含まれないとき、また2に含まれる素性が1の同一階層に含まれないとき
含まれないほうの素性構造にその<素性と値の対>が追加され、1と2は同一構造となる。
(3)リストを素性構造に見立てたユニフィケーションと異なり、要素の出現順位、要素数に関わりなく単一化が可能である。
<例>
%%%%% 素性を指定しての素性値の取り出し |?- X= {a:S,b:c,c:d},X={c:Z}. X = {a:S,b:c,c:d} Z = d yes %%%%% 注意:含まれない素性での素性値の取り出しは追加になる |?- X= {a:S,b:c,c:d},X={q:Z}. X = {a:S,b:c,c:d,q:Z} yes %%%%% 素性値のユニフィケーション、含まれない<素性と値の対>の追加 |?- X= {a:Z,b:c},Y={a:3,d:e},X=Y. X = {a:3,b:c,d:e} Y = {a:3,b:c,d:e} Z = 3 yes %%%%% 素性値が異なりFailする |?- X= {a:{a:b,c:c},b:c},Y={a:3,d:e},X=Y. no %%%%% 素性値に対しても再帰的に素性構造ユニフィケーションが適用される |?- X={a:{b:bb}},Y={a:{c:cc}},X=Y. X = {a:{b:bb,c:cc}}, Y = {a:{b:bb,c:cc}} yes %%%%% Heap上の素性構造とのユニフィケーション(1) a({}). |?- X={b:bb},a(X). X = {b:bb} yes %%%%% Heap上の素性構造とのユニフィケーション(2) b({c:cc}). |?- X={b:bb},b(X). X = {b:bb,c:cc} yes %%%%% Heap上の定義は変わらない |?- listing. a({}). b({c:cc}). yes %% 素性構造の差分表現 % (1)ある素性構造(X)から所定の素性を含まない素性構造(T)の抽出 ?- X={a:b,c:d},X={c:d|T}. T = {a:b} X = {a:b,c:d} yes ?- X={a:b,c:d},X={q:m|T}. T = {a:b,c:d} X = {q:m,a:b,c:d} yes % (2)ある素性構造(T)を差分とする素性構造(X)の表現(生成) ?- T={a:b},X={b:c|T}. X={b:c,a:b}, T={a:b} yes % (3)切り出された差分は、束縛の有無に関わらず別素性構造となり切り出し元に影響を与えない ?- X={b:c|T},T={a:b}. X={b:c} T={a:b} yes ?-X={a:b,c:d},X={c:d|T},T={e:f} X={c:d,a:b} T={a:b,e:f} yes %%%%% 要素の削除 rm_cat({cat:_|T},T). |?- rm_cat({phon:walk,cat:verb,sc:[noun]}, Else). Else = {phon:walk,sc:[noun]} yes %%%%% 構造の変更 change_ha_to({は格:H,と格:T | Else },{は格:T,と格:H | Else }). |?- change_ha_to({と格:ジョンレノン,は格:オノヨーコ, head:結婚した },Ans). Ans = {は格:ジョンレノン, と格:オノヨーコ, head:結婚した } yes
|?- display({a,b,c}),nl. {(a,(b,c))} yes |?- functor({a,b,c},F,A). F = '{}' A = 1 yes
AZ-Prologでは、素性構造を'{}'ファンクタのアリティ3として実装されている。
|?-display({a:b,c:e,f:g}),nl. {(:(a,b),(:(c,e),(:(f,g))))} yes |?-functor({a:b},X,Y). X = '{}' , Y = 3 yes
素性構造は、Univ( =.. ) で分解、生成することができる。 これにより、動的に素性構造を生成することが可能。
(1) |?- A=a,X = {A:B}. はシンタックスエラーだが (2) |?- A=a,X =..['{}',(A:B,_),_,_]. で同様の操作が可能 (3) |?- {a:b,c:d} =.. [F,B,T,S]. Bに素性構造の全要素が ','/2 の項構造で取り出される。 F ='{}', B = (a:b,c:d,_), T = _ S = yes
(2)の第3,4要素、(2),(3)の本体部分 ','/2 項構造の最後の要素は作業変数なので無視する。
ソース ($InstallDir)/system/pl/fs_utility.pl
< 1: 素性と値の対 と 素性,素性値の相互変換>
fs_av/3
(1)Arg1の
<素性と値の対>
からArg2:素性,Arg3:素性値へ分解する
(2)Arg2:素性,Arg3:素性値 から<素性と値の対>を生成する
|?- fs_av(attr:value,A,V). A = attr, V = value yes |?- fs_av(AV,attr,value). AV = attr:value yes
< 2:素性構造とコア構造(','/2)の相互変換 >
fs_body/2
(1) Arg1:素性構造からArg2:コア構造をとりだす
(2) Arg2:コア構造をArg1:素性構造へ変換する
|?- fs_body({a:bb},Core). Core = (a:bb,_) yes |?- fs_body(FS,(a:bb,_)). FS = {a:bb} yes
< 3:動的に素性構造を生成する >
fs_new/3
Arg1を素性名、Arg2を値とする素性構造を生成し、Arg3に単一化する
|?- fs_new(a,aaa,X). X = {a:aaaa} yes |?- Q=q,fs_new(Q,S,X). X = {q:S} yes |?- X={a:b},Q=q,fs_new(Q,S,X). X = {a:b,q:S} yes
< 4:素性構造の全要素をリストに展開する >
fs_list/2
(1)素性構造(Arg1)の全要素をリスト(Arg2)に展開する
(2)リスト(Arg2)を素性構造(Arg1)に変換する
|?- fs_list({category:noun_phrase,number:singular},L). L = [category:noun_phrase,number:singular] yes |?- fs_list(FS,[category:noun_phrase,number:singular]). FS = {category:noun_phrase,number:singular} yes |?-X={a:bb,c:dd},fs_list(X,L),fs_list(X,[e:qq]). X = {a:bb,c:dd,e:qq}, L = [a:bb,c:dd] yes
< 5:ある素性:値が素性構造に含まれているか調べる >
fs_member/2
Arg1調べる要素側の変数は単一化するが リストの要素を調べるmember/2と異なり、Arg2素性構造側は単一化はしない。単一化したい時は、後述のpvalue/3を利用する
|?- fs_member(number:X,{category:noun_phrase,number:singular}). X = singular yes | ?- fs_member(number:X,{category:noun_phrase,number:Y}). X = X_20, Y = Y_22 yes | ?- fs_member(number:d,{category:noun_phrase,number:X}). no | ?- fs_member(number:X,{category:noun_phrase,number:{p:Y}}). X = {p:_20}, Y = Y_22 yes | ?-fs_member(a:{b:bb,c:X},{a:{c:1,b:bb}}). X = 1 yes
< 6:素性構造のコピー >
fs_copy/2
Arg1の素性構造を制約情報を含めた同一構造にコピーしArg2に単一化する
|?- s_constraints_mode(_,on). yes |?- X={a:P#(P=A),b:A},fs_copy(X,Y),Y={a:aa}. X = {a:_75,b:A_63}, P = _75, A = A_63, Y = {a:aaa,b:aaa} yes
< 7: 素性構造の結合 >
fs_append/3
二つの素性構造を元の素性構造は変えず結合した新しい素性構造を生成しArg3に単一化
|?- X={a:S,b:bb},Y={b:Q,e:S},fs_append(X,Y,Z). X = {a:S_62,b:bb}, Y = {b:Q_70,e:S_62}, Z = {b:bb,e:_160,a:_160} yes
単純に二つの素性構造を結合するだけなら、素性構造のユニフィケーションでよい。
|?- X={a:aa},Y={b:bb},X=Y. X= {a:aa,b:bb}, Y= {a:aa,b:bb} yes
< 8:任意個の素性構造を結合 >
fs_appends/2
複数の素性構造を結合した新しい素性構造を生成しArg2に単一化する
|?-X={a:S,b:bb},Y={b:Q,e:Q},fs_appends([X,Y,{a:Q}],D). X = {a:S_70,b:bb}, S = S_70, Y = {b:Q_78,e:Q_78}, Q = Q_78, D = {a:bb,b:bb,e:bb} yes
< 9:素性構造に含まれる素性リストをArg2に単一化する >
pnames/3 (Cu-Prolog 組込互換)
Arg1を素性名、Arg2を値とする素性構造を生成し、Arg3に単一化する
|?- pnames({a:aa,b:bb,c:cc},L). L = [a,b,c] yes
< 10:素性構造(Arg1)に含まれる素性(Arg2)の値をArg3に単一化する >
pvalue/3 (Cu-Prolog 組込互換)
相当する素性がない場合にはfail する
|?- pvalue({a:aa,b:bb,c:cc},b,V). V = bb yes |?- pvalue({a:aa,b:bb,c:cc},q,V). no |?- FS={a:aa,b:bb,c:cc,q:C}, pvalue(FS,q,1). FS = {a:aa,b:bb,c:cc,q:1}, C = 1 yes % 深い階層の素性へのアクセスの簡略表現 |?- pvalue({a:aa,b:{d:dd,q:{f:bb}},c:cc},b:q:f,V). V = bb yes
< 11:素性構造をAVM形式で表示する >
fs_writeAVM/1
fs_writeAVM/1 |?- fs_writeAVM({ 氏名:山田太郎,生年月日:{年:1951,月:5,日:26},趣味:X }). |~ 氏名 山田太郎 ~| | | | |~ 年 1951 ~| | | 生年月日 | 月 5 | | | |_ 日 26 _| | | | |_ 趣味 X _| yes
ICOTで開発された CU-Prologに付属のサンプルプログラム、シンプルHPSGをAZ-Prolog用に書き換えたものが
sample/nluに格納されていますので、これを動かしてみましょう。
%%%%%%%%%% hpsg.pl %%%%%%%%%%%%%% :- ['tree.pl']. % CU-Prologの組込述語 tree/1 をAZ-Prolog用に書いたもの p(Sentence):- parse0(Cat,H,Sentence,[]), nl,tree(H),nl,write('category= '),write(Cat),nl. parse0(MCat,MHist,Str,Rest):- lookup(Str,SubStr,Cat,Hist),!, parse1(Cat,Hist,MCat,MHist,SubStr,Rest). parse1(Cat,H,Cat,H,Str,Str). parse1(LCat,LHist,GCat,GHist,Str,Rest):- psr(LCat,RCat,MCat,RN), parse0(RCat,RHist,Str,SubStr), parse1(MCat,t(t(MCat,RN,[]),LHist,RHist),GCat,GHist,SubStr,Rest). % LeftCategory RightCategory MotherCategory psr({head/H, sc/[RH|PSC],ph/LP},{head/RH,sc/[],ph/RP}, {sc/PSC,head/H,ph/PP},1):-append(LP,RP,PP). psr({head/RH,sc/[], ph/LP},{head/H,sc/[RH|PSC],ph/RP},{sc/PSC,head/H,ph/PP},2):-append(LP,RP,PP). lookup([Word|X],X,{ph/[Word],head/Cat,sc/SC},t(Cat,[Word],[])) :-dict(Word,Cat,SC). dict(mary, noun, []). dict(john, noun, []). dict(meets,verb,[noun,noun]). % : 以下の単語定義略 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
<実行例>
>prolog |?- consult('hpsg.pl'). yes |?- p([mary,meets,john]). {sc/[],head/verb,ph/[mary,meets,john]}---1 |--{sc/[noun],head/verb,ph/[mary,meets]}---2 | |--noun---[mary] | |__verb---[meets] |__noun---[john] category= {sc/[],head/verb,ph/[mary,meets,john]} yes