[Prolog]存在記号∃を表現する

Prologで述語論理の存在記号∃をどうやって表現するのか?

Prologは一階述語論理をベースに作られているから、論理学の色々な記号を表すのは得意だろう、それなら存在記号∃を表すことも出来るのではないだろうかと思いいろいろ調べていたのですが、文法にはそれっぽいのなく、う~んできないのかな…と悩んでいました。

存在記号というのは

「人間の中には血液型がA型の人間が存在する」

などを表す記号です。

もうひとつ「全称記号∀」というのがあるのですが、これは「すべての」を表す記号で、例えば

「すべての人間は、呼吸をする」

などを表す記号です。

これはPrologで表すのは簡単で、

breathe(A):-human(A). % Aが人間であれば呼吸をするという述語

で表すことが出来ます。
Aというのが自由変数になっていてどのような対象にもマッチングするので、「すべての」が表現できるのです。

このプログラムに、例えば

human(taro). % taroは人間であるという記述

と追記し、プロンプトで

breathe(A).

と入力すると

A = taro.

(呼吸するのはtaro)
という返答が返ってきます。

そして始めの疑問に戻りますが、
上記の存在記号∃バージョンは果たしてどのように記述するのか。

「人間の中には血液型がA型の人間が存在する」

はどうPrologで記述するのか。

yahooの知恵袋や外国のstackoverflowで英語で質問してみたのですが、回答が得られず、あきらめかけていたのですが、以下のページを見てやっとわかりました。

Convert first-order logic expressions to normal form

このページの

4. Somebody has a car.



person(sk10).
car(sk11).
has(sk10, sk11).

に変換されているのを見てやっと分かりました

「なんでもいいから、何か」を登録すれば良いのです

human(someone).
bloodtype_A(someone).

これでOKです。

someoneというのはいったい誰なのか、それは自分もわからない。
とにかくそれは人間です。
そして、bloodtype_A(_)を満たす人間someoneが、間違いなく存在する。
これで∃の条件は満たされます。
ここで引数に自由変数を使ってしまうと、上で述べた「すべての」になってしまうのでアウトです。

さらにいうと、色々な述語をつくるときに、この someone という同じアトムを使いまわすとおかしくなる
(someoneは血液型がAでしかもBでとかになる)
ので、someoneの後ろに添え字を追加し、述語ごとにユニークにする必要があるでしょう。

気付けば単純なことなんだけど、なかなか思い至らなかった。

[Prolog]動的に述語定義する

以下のように記述すると動的に任意の述語を定義できる。カンマで区切って複数述語呼び出すようなものもできる。

javascriptも文字列から関数定義とかできるけど、そんな感じに使えそうだ。

こういう機能は普通は使わないけどしかるべき時に使うとすごいプログラムが書けると思う。

セキュリティホールになりそうな気も、、、

動的に

test(X):-
X1 is X+1,
X2 is X1+1,
write(X2).

という述語を作成したい場合(意味無いけど)

assert(test(X):- (X1 is X+1, X2 is X1+1, write(X2))).

[prolog]attributed_variable に関して

prologは基本的には自由変数には1度しか値を入力できず、処理の途中で書き換えることはできない。

唯一書き換えられるタイミングはバックトラックで、バックトラックが発生するとchoice point以降の処理で設定した自由変数が全て未設定の状態になる。

しかし、attributed variableという、自由変数の属性値のような変数があり、これは変更が可能なようだ。
これは結び付けられた自由変数が設定されるとunify_hookというゴールが駆動されるようになっているようだ。
しかもattributed variableは変更の履歴情報を持っており、バックトラックで以前の値が復活されるような感じらしい。
ちょっとまだ仕組みが良く分からないが、clpfdライブラリのソースをみるとこの仕組みが多用されているのがわかる。

他にfreeze,meltという述語があり、これも自由変数に値を設定するとfreezeにより結び付けられたgoalが自動的に実行されるような仕組みになっている。whenとかいう述語はfreezeの条件付バージョンのようだ。

freeze,meltは30年くらい前の書籍であるart of prologにも記述があるので、かなり歴史のある述語のようだ。
freeze,meltはclpfdのソースでは使っていないようだ(→改めてgrepしてみると使ってる場所ありました 2016/10/04)

clpfdのソースは自分にとってはかなり難解で、Prologの基本レベルの文法を押さえただけではまったく理解することが出来ない。
気長に解析していこうと思う。

[PROLOG] compound term

Art of Prolog を読んでいて、Equation Solver(方程式ソルバー)を作るという記事があり、ふむふむと読んでいた。

その中で、Prologを勉強している人には常識かもしれないのですが、以下の様な記述があり「??」となった

方程式を解く際の移項を行う術語定義で
ikou(A+B=C,A=C-B).

などの記載や、

T=a*b^c

など。

例えば後者のTには一体何が入っているのか?

[a , * , b , ^ , c]
というリストなのか?

実際にSWI-PROLOGで問い合わせて調べてみた。

3 ?- T = a*b^c,atomic(T).
false.

atomicではない。

4 ?- T = a*b^c,atom(T).
false.

atomではない

5 ?- T = a*b^c,var(T).
false.

変数ではない。

8 ?- T = a*b^c,string(T).
false.

stringではない

10 ?- T = a*b^c,compound(T).
T = a*b^c.

というわけで、compound(複合項)らしい。

ちなみに

14 ?- T = a*b^c,T = [First|Rest].
false.

15 ?- T = a*b^c,T = [].
false.

リストへのマッチングが失敗するので、リストではない。

また、

16 ?- T = a*b^c,callable(T).
T = a*b^c.

callableが成功するので、実行可能な述語として認識されている。

それでは 複合項をリストに変換する =.. を使用して調べてみよう。

18 ?- T = a*b^c,T=..List.
T = a*b^c,
List = [*, a, b^c].

Listにした場合、 「*」 「a」 「b^c」 の3つの要素を持つリストとなる。

というわけで a*b^c という記述は 「*という演算子の引数として aとb^c を渡した*(a,b^c)みたいな複合項」 という
意味だということがわかった。

演算子の優先順位は
* 400
^ 200
で ^ のほうが高いため * のほうが外側の演算子ということで b^c はくっついているのであろう。
さらにb^cも枝の部分の複合項として認識されるので、
a*b^c = T という記述は、自動的に *(a, ^(b,c)) みたいな木構造の複合項として認識されているということがわかった。
どちらの演算子が親になるかはおそらく演算子の優先順位で決まるのであろう(実験およびマニュアル確認はしてません)

ちなみに、以下のようにした場合は

21 ?- T=a+b+c+d,T=..List.
T = a+b+c+d,
List = [+, a+b+c, d].

となるので、あくまで一番上位のレベルでは最後の演算子のみ認識され、それ以前の要素は枝として認識されている。
演算子の引数は2つのみということだろう。

これを利用して、以下のように 微分の導関数を求める述語も作れるようだ(実際はsin(x)の微分の定義など他のいろいろな述語定義が追加されます)

d( U^C, X, C*A*U^(C-1) ):-
atomic(C),
C\=X,
d( U, X, A ).

prolog derivative で検索するといくつかサンプルのソースがヒットするので興味のある人は探してみてください。

自分も興味を持ったので微分をする述語を作ってみようと思います。
完成したらこのブログに書きます(挫折するかも…)

Art of Prolog

Art of Prologをちまちまと読んでいます。

邦訳の「Prologの技芸」は高価かつレアでなかなか手に入りませんが、英語版は検索するとpdfが無料でダウンロードできます。

PrologのDCGは多分必須の知識だと思うけど、自分が調べた限りでは日本語のwebの情報はなく英語の解説サイトを読んで勉強しました。

このArt of PrologにもDCGのことが書いてありました。日本語でDCGのこと勉強したい人は「Prologの技芸」以外の資料は多分、ない(2016年5月現在)

希望する人がいれば僕が解説書いても良いです。

Prologは大学のとき授業でやってぜんぜんわからず憧れだけあって社会人になって真剣に勉強し始めましたが、現在は
CLPFDやAttributed VariableやCHRなど、大学の授業で学んだときより格段にパワーアップしている印象です。しかし日本ではPrologは超下火なので全然情報がない。必然的に英語を読まねばなりません。

Prolog好きな人たち(理屈っぽい文法オタクな人ではなく、Prologだとすごくパワフルに扱える事象に興味がある人)とつながりたいものだ。

簡単な巡回セールスマン問題をPrologで解く

「鉄道のスケジューリングアルゴリズム」という本を買って読んでいます。
その中で以下のような簡単な巡回セールスマン問題があったのでPrologでさくっと解いてみました。

あなたの会社は東京にある。札幌、大阪、博多、那覇にそれぞれクライアントを抱えており、それぞれの都市間の距離は以下である。
東京から出発してクライアント全員を1回づつ訪問し、東京に戻らなければいけない。このとき、移動距離の総和が2900km以下であるような経路は存在するだろうか?

sapporo-tokyo,511km
sapporo-osaka,667km
sapporo-hakata,883km
sapporo-naha,1398km
tokyo-osaka,278km
tokyo-hakata,566km
tokyo-naha,984km
osaka-hakata,289km
osaka-naha,740km
hakata-naha,537km

——————— prologのコード ———————-

distance(sapporo,tokyo,511):-!.
distance(sapporo,osaka,667):-!.
distance(sapporo,hakata,883):-!.
distance(sapporo,naha,1398):-!.
distance(tokyo,osaka,278):-!.
distance(tokyo,hakata,566):-!.
distance(tokyo,naha,984):-!.
distance(osaka,hakata,289):-!.
distance(osaka,naha,740):-!.
distance(hakata,naha,537):-!.
distance(X,Y,Dist):-distance(Y,X,Dist),!.

solve_salesman:-
Cities = [sapporo,osaka,hakata,naha],
make_houmonjun(Cities,HoumonLstInit),
append([tokyo],HoumonLstInit,HoumonLst2),
append(HoumonLst2,[tokyo],HoumonLst3),
calc_distance(HoumonLst3,Distance),
write(HoumonLst3),write(' '),write(Distance),nl,
Distance =< 2900,
write('Success! Houmonjun:'),write(HoumonLst),write(' distance:'),write(Distance).

make_houmonjun([],[]):-!.
make_houmonjun(Cities,[SelectedCity|RestSelected]):-
select(SelectedCity,Cities,RestCities),
make_houmonjun(RestCities,RestSelected).

calc_distance([_],0):-!.
calc_distance([FirstCity,SecondCity | RestCities],DistSum):-
!,
distance(FirstCity,SecondCity,CurrentDistance),
calc_distance([SecondCity | RestCities],OtherDistSum),
DistSum is CurrentDistance + OtherDistSum.


-------------- 実行結果 -----------------

[4] 27 ?- solve_salesman.
3 ?- solve_salesman.
[tokyo,sapporo,osaka,hakata,naha,tokyo] 2988
[tokyo,sapporo,osaka,naha,hakata,tokyo] 3021
[tokyo,sapporo,hakata,osaka,naha,tokyo] 3407
[tokyo,sapporo,hakata,naha,osaka,tokyo] 2949
[tokyo,sapporo,naha,osaka,hakata,tokyo] 3504
[tokyo,sapporo,naha,hakata,osaka,tokyo] 3013
[tokyo,osaka,sapporo,hakata,naha,tokyo] 3349
[tokyo,osaka,sapporo,naha,hakata,tokyo] 3446
[tokyo,osaka,hakata,sapporo,naha,tokyo] 3832
[tokyo,osaka,hakata,naha,sapporo,tokyo] 3013
[tokyo,osaka,naha,sapporo,hakata,tokyo] 3865
[tokyo,osaka,naha,hakata,sapporo,tokyo] 2949
[tokyo,hakata,sapporo,osaka,naha,tokyo] 3840
[tokyo,hakata,sapporo,naha,osaka,tokyo] 3865
[tokyo,hakata,osaka,sapporo,naha,tokyo] 3904
[tokyo,hakata,osaka,naha,sapporo,tokyo] 3504
[tokyo,hakata,naha,sapporo,osaka,tokyo] 3446
[tokyo,hakata,naha,osaka,sapporo,tokyo] 3021
[tokyo,naha,sapporo,osaka,hakata,tokyo] 3904
[tokyo,naha,sapporo,hakata,osaka,tokyo] 3832
[tokyo,naha,osaka,sapporo,hakata,tokyo] 3840
[tokyo,naha,osaka,hakata,sapporo,tokyo] 3407
[tokyo,naha,hakata,sapporo,osaka,tokyo] 3349
[tokyo,naha,hakata,osaka,sapporo,tokyo] 2988
false.

2900km以下の経路は存在しないようですね。

今回は経路が4!とおりで網羅が簡単だったけど、実際の問題はこれよりうんと複雑だろう。
自分は健康診断の会社で9年間働いていたのですが、いろいろな会社を回る健診バスの運行経路を決めるのは道路状況やリソース(人、機械)の問題もありかなり複雑そうでした。

小町算のパズルを制約論理プログラムとDCGを使用して解く

自分がPrologの勉強のために参考にしているM.Hiroiさんのページで掲載されている小町算の問題を、広井さんの解法とは異なり制約論理プログラミングと最近勉強したDCGを使用して解いてみました。
(参照:http://www.geocities.jp/m_hiroi/prolog/prolog14.html)

問題:
小町算
1 から 9 までの数字を順番に並べ、間に + と – を補って 100 になる式を作ってください。
例:1 + 2 + 3 – 4 + 5 + 6 + 78 + 9 = 100

——————- Prologのコード ———————–

:-use_module(library(clpfd)).

solve_komachi:-
phrase(komachi(RetNumList),[1,2,3,4,5,6,7,8,9],[]),
length(RetNumList,Len),
length(SignLst,Len),
SignLst ins -1\/1,
SignLst = [1 | Rest],
maplist(mul,RetNumList,SignLst,SignedNumList),
sum(SignedNumList,#=,100),
label(SignedNumList),
write(SignedNumList),nl.

mul(A,B,C):-
C #= A*B.

komachi([])-->[].
komachi([LeftListNum|RestNumList])-->{between(1,9, Len),length(LeftList,Len)},LeftList,{tonum(LeftList,LeftListNum)},komachi(RestNumList).

tonum(List,Num):-
length(List,Digit),
tonum(List,Num,Digit).

tonum([Num],Num,1).
tonum([First|Rest],Num,Digit):-
ThisDigitNum is First * 10 ^(Digit - 1),
NextDigit is Digit - 1,
tonum(Rest,RestNum,NextDigit),
Num is ThisDigitNum + RestNum.

———— 実行結果 ————–
[7] 35 ?- solve_komachi.
[1,2,3,-4,5,6,78,9]
[1,2,34,-5,67,-8,9]
[1,23,-4,5,6,78,-9]
[1,23,-4,56,7,8,9]
[12,-3,-4,5,-6,7,89]
[12,3,4,5,-6,-7,89]
[12,3,-4,5,67,8,9]
[123,-4,-5,-6,-7,8,-9]
[123,4,-5,67,-89]
[123,45,-67,8,-9]
[123,-45,-67,89]

広井さんのページ

自分はM.hiroiさんのページでPrologの基礎を学びその後Swi-Prologの英文マニュアルを読み制約論理プログラミングなど知ったのですが、広井さんにメールで制約論理プログラミングを紹介したら広井さんも興味を持たれたようで早速ホームページで取り上げられていました。

http://www.geocities.co.jp/SiliconValley-Oakland/1680/memo.html

僕の名前も紹介者として掲載されてました。ワーイ

前回からの続き Zebra Puzzleを制約論理プログラミングで解く2

前回の問題よりさらに複雑な以下のZebra Puzzleを制約論理プログラミングで解いてみる
参照:https://en.wikipedia.org/wiki/Zebra_Puzzle

問題:
The following version of the puzzle appeared in Life International in 1962:

There are five houses.
The Englishman lives in the red house.
The Spaniard owns the dog.
Coffee is drunk in the green house.
The Ukrainian drinks tea.
The green house is immediately to the right of the ivory house.
The Old Gold smoker owns snails.
Kools are smoked in the yellow house.
Milk is drunk in the middle house.
The Norwegian lives in the first house.
The man who smokes Chesterfields lives in the house next to the man with the fox.
Kools are smoked in the house next to the house where the horse is kept.
The Lucky Strike smoker drinks orange juice.
The Japanese smokes Parliaments.
The Norwegian lives next to the blue house.
Now, who drinks water? Who owns the zebra?

In the interest of clarity, it must be added that each of the five houses is painted a different color, and their inhabitants are of different national extractions, own different pets, drink different beverages and smoke different brands of American cigarets [sic]. One other thing: in statement 6, right means your right.

————————- Prologのプログラム ——————————

:-use_module(library(clpfd)).

zebra_prob:-

% left ------> right
House=[Color1,Color2,Color3,Color4,Color5],
Pet=[Pet1,Pet2,Pet3,Pet4,Pet5],
Race=[Race1,Race2,Race3,Race4,Race5],
Drink=[Bev1,Bev2,Bev3,Bev4,Bev5],
Smoke=[Cig1,Cig2,Cig3,Cig4,Cig5],

% Race 1:English 2:Spaniard 3:Ukrainian 4:Norwegian 5:Japanese
% HouseColor 1:red 2:green 3:ivory 4:yellow 5:blue
% Pet 1:dog 2:snails 3:fox 4:horse 5:zebra
% Drink 1:coffee 2:tea 3:milk 4:orange juice 5:water
% Smoke 1:Old Gold 2:Kool 3:Chesterfields 4:Lucky Strike 5:Parliaments

all_different(House), %それぞれ異なる色の家
all_different(Pet), %それぞれ異なるペット
all_different(Race), %それぞれ異なる国籍
all_different(Drink), %それぞれ異なる飲料
all_different(Smoke), %それぞれ異なるタバコ

House ins 1..5,
Pet ins 1..5,
Race ins 1..5,
Drink ins 1..5,
Smoke ins 1..5,

% The Englishman(1) lives in the red house(1).
Race1 #= 1 #<==> Color1 #= 1,
Race2 #= 1 #<==> Color2 #= 1,
Race3 #= 1 #<==> Color3 #= 1,
Race4 #= 1 #<==> Color4 #= 1,
Race5 #= 1 #<==> Color5 #= 1,

% The Spaniard(2) owns the dog(1).
Race1 #= 2 #<==> Pet1 #= 1,
Race2 #= 2 #<==> Pet2 #= 1,
Race3 #= 2 #<==> Pet3 #= 1,
Race4 #= 2 #<==> Pet4 #= 1,
Race5 #= 2 #<==> Pet5 #= 1,

% Coffee(1) is drunk in the green house(2).
Bev1 #= 1 #<==> Color1 #= 2,
Bev2 #= 1 #<==> Color2 #= 2,
Bev3 #= 1 #<==> Color3 #= 2,
Bev4 #= 1 #<==> Color4 #= 2,
Bev5 #= 1 #<==> Color5 #= 2,

% The Ukrainian(3) drinks tea(2).
Race1 #= 3 #<==> Bev1 #= 2,
Race2 #= 3 #<==> Bev2 #= 2,
Race3 #= 3 #<==> Bev3 #= 2,
Race4 #= 3 #<==> Bev4 #= 2,
Race5 #= 3 #<==> Bev5 #= 2,

% The green house(2) is immediately to the right of the ivory house(3).
Color1 #= 3 #<==> Color2 #= 2 ,
Color2 #= 3 #<==> Color3 #= 2 ,
Color3 #= 3 #<==> Color4 #= 2 ,
Color4 #= 3 #<==> Color5 #= 2 ,

% So, green house(2) is not leftmost.
Color1 #\= 2,

% The Old Gold(1) smoker owns snails(2).
Cig1 #= 1 #<==> Pet1 #= 2,
Cig2 #= 1 #<==> Pet2 #= 2,
Cig3 #= 1 #<==> Pet3 #= 2,
Cig4 #= 1 #<==> Pet4 #= 2,
Cig5 #= 1 #<==> Pet5 #= 2,

% Kools(2) are smoked in the yellow(4) house.
Cig1 #= 2 #<==> Color1 #= 4,
Cig2 #= 2 #<==> Color2 #= 4,
Cig3 #= 2 #<==> Color3 #= 4,
Cig4 #= 2 #<==> Color4 #= 4,
Cig5 #= 2 #<==> Color5 #= 4,

% Milk(3) is drunk in the middle house.
Bev3 #= 3,

% The Norwegian(4) lives in the first house. (I assume that "first" means leftmost)
Race1 #= 4,

% The man who smokes Chesterfields(3) lives in the house next to the man with the fox(3).
(Cig1 #= 3 #/\ Pet2 #= 3) #\/
(Cig2 #= 3 #/\ Pet3 #= 3) #\/
(Cig3 #= 3 #/\ Pet4 #= 3) #\/
(Cig4 #= 3 #/\ Pet5 #= 3) #\/
(Pet1 #= 3 #/\ Cig2 #= 3) #\/
(Pet2 #= 3 #/\ Cig3 #= 3) #\/
(Pet3 #= 3 #/\ Cig4 #= 3) #\/
(Pet4 #= 3 #/\ Cig5 #= 3) ,

% Kools(2) are smoked in the house next to the house where the horse(4) is kept.
(Cig1 #= 2 #/\ Pet2 #= 4) #\/
(Cig2 #= 2 #/\ Pet3 #= 4) #\/
(Cig3 #= 2 #/\ Pet4 #= 4) #\/
(Cig4 #= 2 #/\ Pet5 #= 4) #\/
(Pet1 #= 4 #/\ Cig2 #= 2) #\/
(Pet2 #= 4 #/\ Cig3 #= 2) #\/
(Pet3 #= 4 #/\ Cig4 #= 2) #\/
(Pet4 #= 4 #/\ Cig5 #= 2) ,

% The Lucky Strike(4) smoker drinks orange juice(4).
Cig1 #= 4 #<==> Bev1 #= 4,
Cig2 #= 4 #<==> Bev2 #= 4,
Cig3 #= 4 #<==> Bev3 #= 4,
Cig4 #= 4 #<==> Bev4 #= 4,
Cig5 #= 4 #<==> Bev5 #= 4,

% The Japanese(5) smokes Parliaments(5).
Race1 #= 5 #<==> Cig1 #= 5,
Race2 #= 5 #<==> Cig2 #= 5,
Race3 #= 5 #<==> Cig3 #= 5,
Race4 #= 5 #<==> Cig4 #= 5,
Race5 #= 5 #<==> Cig5 #= 5,

% The Norwegian(4) lives next to the blue house(5).
(Race1 #= 4 #/\ Color2 #= 5) #\/
(Race2 #= 4 #/\ Color3 #= 5) #\/
(Race3 #= 4 #/\ Color4 #= 5) #\/
(Race4 #= 4 #/\ Color5 #= 5) #\/
(Color1 #= 5 #/\ Race2 #= 4) #\/
(Color2 #= 5 #/\ Race3 #= 4) #\/
(Color3 #= 5 #/\ Race4 #= 4) #\/
(Color4 #= 5 #/\ Race5 #= 4) ,

label(House),
label(Pet),
label(Race),
label(Smoke),
label(Drink),

write('house:'),write(House),nl,
write('pet:'),write(Pet),nl,
write('race:'),write(Race),nl,
write('drink:'),write(Drink),nl,
write('smoke:'),write(Smoke),nl,nl.
 
--------------- 実行結果 ----------------------
[3] 23 ?- zebra_prob.
house:[4,5,1,3,2]
pet:[3,4,2,1,5]
race:[4,3,1,2,5]
drink:[5,2,3,4,1]
smoke:[2,3,1,4,5]

true ;
false.

———– 解説 ————

% Race 1:English 2:Spaniard 3:Ukrainian 4:Norwegian 5:Japanese
% HouseColor 1:red 2:green 3:ivory 4:yellow 5:blue
% Pet 1:dog 2:snails 3:fox 4:horse 5:zebra
% Drink 1:coffee 2:tea 3:milk 4:orange juice 5:water
% Smoke 1:Old Gold 2:Kool 3:Chesterfields 4:Lucky Strike 5:Parliaments

Now, who drinks water? Who owns the zebra?
5:water を飲んでいるのは 4:Norwegian
5:zebra を飼っているのは 5:Japanese

単純に条件を書いていけば簡単にプログラミングできたのですが、”next to” の制約を書く書き方で若干間違えた(解が増えてしまった)のとスペルミスなどのチェックが面倒くさかった。

ゼブラパズル(Zebra Puzzle)を制約論理プログラミングで解く

以下のパズル(ゼブラパズルというらしい)を制約論理プログラミングで解いてみた。

問題:
1.家が3軒あります。その3軒の家はそれぞれ赤・青・緑で塗られています。そしてその住人は、それぞれ異なる国籍で、それぞれ異なるペットを買っています。
2.イギリス人は赤い家に住んでいます。
3.スペイン人は犬を飼っています。
4.日本人は、猫を飼っている人の右側に住んでいます。
5.猫を飼っている人は青色の家の左に住んでいます。

誰がシマウマを飼っているでしょうか?

—————-Prologのコード——————

:-use_module(library(clpfd)).

zebra_prob:-

% left ------> right
House=[Color1,Color2,Color3],
Pet=[Pet1,Pet2,Pet3],
Race=[Race1,Race2,Race3],

% Race 1:Japanese 2:English 3:Spanish
% Color 1:Red 2:Blue 3:Green
% Pet 1:Cat 2:Dog 3:Zebra

all_different(House), %それぞれ異なる色の家
all_different(Pet), %それぞれ異なるペット
all_different(Race), %それぞれ異なる国籍

House ins 1..3,
Pet ins 1..3,
Race ins 1..3,

%イギリス人(2)は赤い家(1)
Color1 #= 1 #<==> Race1 #= 2,
Color2 #= 1 #<==> Race2 #= 2,
Color3 #= 1 #<==> Race3 #= 2,

%スペイン人(3)は犬(2)を飼っている
Race1 #= 3 #<==> Pet1 #= 2,
Race2 #= 3 #<==> Pet2 #= 2,
Race3 #= 3 #<==> Pet3 #= 2,

%日本人(1)は、猫(1)を飼っている人の右側
Pet1 #= 1 #<==> Race2 #= 1,
Pet2 #= 1 #<==> Race3 #= 1,
Race1 #\= 1, %日本人は一番左ではない

%猫(1)を飼っている人は青色の家(2)の左に住んでいます。
Pet1 #= 1 #<==> Color2 #= 2,
Pet2 #= 1 #<==> Color3 #= 2,
Pet3 #\= 1, %猫は一番右ではない

label(House),
label(Pet),
label(Race),
write('house:'),write(House),nl,
write('pet:'),write(Pet),nl,
write('race:'),write(Race).

———–実行結果———–

1 ?- zebra_prob.
house:[1,2,3]
pet:[1,3,2]
race:[2,1,3]
true ;
house:[3,1,2]
pet:[2,1,3]
race:[3,2,1]
true.

解説
Race 1:Japanese 2:English 3:Spanish
Color 1:Red 2:Blue 3:Green
Pet 1:Cat 2:Dog 3:Zebra
PetがZebra(3)と同じ要素番号のRaceは必ず1(日本人)
よってシマウマは日本人が飼っている。
家、ペット、国籍 の並びの組み合わせは上記2パターンある。

※ 「~の右に」という文章を「1軒右」と解釈しています。
  (2軒右も含むというコードに変更するのも簡単です)