図3.1 素性構造の例 名前 −−−−→ダンスインザダーク | 系統 →競走馬−−−−−→Hail to Reason |\ 父 系統 ↑ | −−→種牡馬−−−− | | 名前 | −−−→サンデーサイレンス | 母 名前 −−→繁殖牝馬−−−−→ダンシングキイ | 系統 −−−→Northern Dancer
一番左の矢印で指されているノードは,根(root)と呼ばれる特別なノードで, どんな素性構造にも必ず一つ存在するノードです. また,型'Hail to Reason'がついているノードは, 二つのエッジの先となっています.このような構造を, 構造共有(後述)と呼びます.
このようなグラフをいちいち書くのは面倒なので,
一般に次のような
図3.2 素性構造のAVM表記の例 |~競走馬 ~| | 名前:ダンスインザダーク | | 系統:$1 Hail to Reason | | |~種牡馬 ~| | | 父:| 名前:サンデーサイレンス | | | |_系統:$1 _| | | |~繁殖牝馬 ~| | | 母:| 名前:ダンシングキイ | | |_ |_系統:Northern Dancer_| _|
これは,初めの図と同じ構造を表しています. 角かっこで囲まれた部分が,素性構造です. かっこ内の左上に書かれているものが,その素性構造の根の型で,その下に, それぞれの素性と,その素性の先の素性構造を書きます. 構造共有は,ダグ(上の図では, "$1")で表します(詳しくは後述).LiLFeSが素性構造を画面に表示する時も, この記述方法を使います.
LiLFeSでは,各素性の先に来る素性構造の根の型を指定することができます. 例えば,上の例では, 素性'父'の先のノードは型'種牡馬'であるとすれば, '競走馬'や'Hail to Reason'が来ることができなくなります.
図3.3 型階層の例 正三角形 直角二等辺三角形 \ / \ 二等辺三角形 直角三角形 \ / 三角形 | bot
線で結ばれた関係はsupertype-subtypeの関係と呼ばれ,上の図では,
下側がsupertype,上側がsubtypeになっています.
全ての型('bot'を除く)は,
必ず一つ以上のsupertypeを持ちます.
supertypeはsubtypeを汎化したもの,
subtypeはsupertypeを特化したものと呼ばれ,上の例では,
supertypeの概念を特殊化した概念が,subtypeになるようにしています.
型階層は,このような特殊化・一般化の関係を記述するのに使うのが一般的です.
また,supertypeを遡っていった型,subtypeをたどっていった型も,
それぞれsupertype, subtypeと呼ばれます.
上の図の一番下にある型
'bot
型と型との間の演算として,
二つの型をそれぞれX,Yとすると,単一化は,LiLFeSでは
X = Y
で表されます(=
は中おきの述語).例えば,
上記の三角形の例を使うと,
となります.共通のsubtypeが無い時は,失敗します.また, 'bot'は全ての型のsupertypeなので, どんな型Xとの単一化も成功し, 結果はXになります.
> ?- X = 三角形, X = 二等辺三角形. X: 二等辺三角形 > ?- X = 二等辺三角形, X = 直角三角形. X: 直角二等辺三角形 > ?- X = 正三角形, X = 直角三角形. no
LiLFeSの型階層では, 単一化の結果(LUB)は一意に定まらなければならないという制約があります. つまり,二つの型の共通のsubtypeが複数あって, しかもその中にsupertype-subtypeの関係に無い型の組があるような階層構造は, 定義できないということです.例えば, 上の例で'二等辺三角形'と'直角三角形' とを直接のsupertypeとする新たな型Aを定義しようとすると, エラーになります.このような制限があるため, PROLOGの述語による表現より弱い表現になるわけですが,そのおかげで, 型の単一化の処理は高速に行なわれます.
各型は,
例として,次のような型階層を考えます.角かっこ内が, その型で新たに定義される素性です.
表3.4 素性の例 ストレート[最小の数] フラッシュ[スート] \ / ポーカーの役 | bot
ここで,'ストレート'と'フラッシュ'をsupertypeとする型 'ストレートフラッシュ'を定義すると,それは, '最小の数'と'スート'という2つの素性を初めから持っていることになります. もし型'ポーカーの役'に何らかの素性Aがあったとすると, それは'ストレート'と'フラッシュ'にも受け継がれ, さらに'ストレートフラッシュ'には,両方の直接のsupertypeから, 同じ素性Aを受け継ぐことになります.このときは, 素性Aは一つに合併され,結局, 'ストレートフラッシュ'は3つの素性を持つようになります.
素性構造の単一化は,まず,根の型を単一化することから始まります. それに成功したら,つぎに,各素性をたどっていき, その先の素性構造を単一化します.この操作を再帰的に行ない, 全てのノードで単一化が成功すると, 全体の素性構造の単一化が成功したことになります.
ポーカーの役の例を使って, 次のような2つの素性構造の単一化を考えてみます.
まず,根の型'ストレート'と'フラッシュ'とを単一化します. これの結果は'ストレートフラッシュ'になるので, それぞれの素性構造は,次のようになります.
|~ストレート ~| |_最小の数:10_| |~フラッシュ ~| |_スート:スペード_|
次に,各素性について,その先の素性構造を単一化します. 素性'最小の数'の素性構造は型'10'になり, 素性'スート'の素性構造は型'スペード'になります.そして, 全てのノードの単一化が成功したので,次のような結果が得られます.
|~ストレートフラッシュ~| | 最小の数:10 | |_スート:bot _| |~ストレートフラッシュ~| | 最小の数:bot | |_スート:スペード _|
|~ストレートフラッシュ~| | 最小の数:10 | |_スート:スペード _|
素性構造は,一般的なグラフ構造を有することができます.木構造との違いは,
複数のエッジの先が一つのノードになる可能性があるということです.
このような構造を,
構造共有を含む素性構造の単一化は, 前節の方法がそのまま適用できます.ただ, 構造共有が起きているという情報も,単一化されます. 例えば,次のような2つの素性構造を単一化する場合,
素性'A'を単一化した時, 素性'A'と素性'B'の先は構造共有されているという情報が, 下の構造にも伝えられます.同様に, 下の構造では素性'B'と素性'C'とが構造共有されているので, それが上の素性構造にも伝えられます.結局, 単一化の結果は次のようになります.
|~example ~| | A:$1 bot | | B:$1 | |_C:bot _| |~example ~| | A:bot | | B:$2 bot | |_C:$2 _|
|~example ~| | A:$1 bot | | B:$1 | |_C:$1 _|
LiLFeSでは,PROLOGと同様に,リストを扱うことができます.
> ?- X = [1, 2, 3]. X: < 1, 2, 3 >
リストは,実際には素性構造になっています.
リストの型は'list
'で,
直接のsubtypeに'cons
'と'nil
'という型を持ちます.
また,'cons
'は,
'hd
'と'tl
'という素性を持ちます.
Lispの言葉を借りれば,'hd
'は'car
'にあたり,
'tl
'は'cdr
'にあたります.従って,
上の例のリストは,次のような素性構造であるということになります.
|~cons ~| | hd:1 | | |~cons ~| | | | hd:2 | | | tl:| |~cons ~| | | | | tl:| hd:3 | | | |_ |_ |_tl:nil_|_|_|
素性構造ですから,構造共有も表現できます.例えば,次のようにして, 1と2が交互に無限に続く列を表現することができます.
> ?- X = [1, 2 | X]. X: [$1] < 1, 2 | [$1] ... >
LiLFeSでは,述語や素性名なども,全て素性構造として扱われています.