このページではJavaScriptを使用しています。

9-2.素性構造型(Feature Structure型)

9-2-1.概要

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

9-2-2.組込述語

fs_mode/2 ,fs_delimiter/2 ,fstructure/1  の3述語が新規に追加されています。
組込述語
fs_mode(I1?,I2+)

I1,I2とも、0,1,2のいずれかの整数
I1 現在の素性構造モード
I2 設定する素性構造モード


0: 素性構造型を利用しません。(従来と同じ。V8までとの互換性のために用意されています。)
1: 素性構造型を利用します。
2: 素性構造型を利用します。デバッグモード


初期値は 1 に設定されています。

<注意> 本述語は、素性構造型を利用せず、かつ素性構造型に抵触する書式の従来プログラムがある場合に 従来シンタックスを適用するために用意されています。

<例>

:- fs_mode(_,0).
fs_delimiter(A1?,A2+)

A1 : 現在の素性名と素性値の組を区切る中置きオペレータ名がユニファイされます。
A2 : 設定する新しい素性名と素性値の組を区切る中置きオペレータ名
中置きオペレータでない場合は同時にオペレータ宣言をしておきます。
初期値は ":" 結合値=550,xfy。

<注意>
本述語は、CU-Prologのソースを利用するなど特別に必要な場合、そのソースファイルの先頭に切り替えを宣言します。その際、ユーザーの作成するプログラム中に現れる区切り記号もこれに統一しておかねばなりません。 動的、頻繁に切り替えるのは混乱と誤動作をまねくので慎むべきです。

<例>

:- fs_delimiter(_,!).
:- op(550,xfy,!).
a({a!b,c!d}).
fstructure(+Term)

Term : 調べたい項
Termが素性構造型であるときのみ成功します。

<例>

 |?- X={a:{b:bb,c:cc}},fstructure(X).
 yes

9-2-3.素性構造のユニフィケーション

「素性構造を取り扱うモード」(デフォルト)と 「素性構造を取り扱わないモード」では中括弧で括られたデータ同士のユニフィケーションの振る舞いが異なるので注意しましょう。

・素性構造は素性構造または変数とのみユニフィケーションします。他の型とでは失敗します。
・素性構造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

9-2-4.素性構造型のメタ述語適用

通常の中括弧は'{}'ファンクタのアリティ1です。
|?- 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 項構造の最後の要素は作業変数なので無視します。

9-2-5.素性構造型操作のユーティリティ(コンパイル組込述語)

ソース ($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:aaa}
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:aa,b:aa}
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

9-2-6.素性構造型を使った構文解析プログラム例

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