以上/* ここでは Prolog の働きを簡単に示しながら,家系図作成や近縁・近交係数計算プログラムを 作ってきた過程の概略を紹介する. 青色部分は注釈で,なくても可. 実際に動かすには,この部分だけ(さわり〜以上の間)を切り取ったファイルを AZ-Prolog に読み込み ( ?- とでたら ['ファイル名'].で),あるいはソフネック社 AZ-Prolog おためし版 の“プログラム入力欄”にコピー・ ペーストして(動くの確認済み),以下のように入力して haha(kosihikari). sosen(kosihikari). sosen2(kosihikari). ・・・ などを走らせるのだが,以下のテキストを眺めて 鑑賞だけでも して下さい. 両親データは下部にあり 例; rr('kosihikari','norin22','norin1'). 1) まず,A の母は rr(A,X,_) の X でその X を書け,が以下. */ haha(A):-rr(A,X,_),write(X). /* ここでのデータで実行するため, ?-haha(kosihikari). と入力すると,その結果は, norin22 と出力されます.つまりコシの母は 農林22号. 2) 次に,その母,またその母…をするため,A の母はデータ rr(A,X,_) の X,その X を書き, 次に X の母を捜すため又 sosen を呼び出し,そのまた母を書き,さらに…と無限に, 母がなくなるまで,がわずか以下の一行でOK. */ sosen(A):-rr(A,X,_),write(X),sosen(X). /* ?-sosen(kosihikari). と入力して実行すると, norin22norin8aikoku と出力されるが,品種名が続いて表記され見にくいので, 3) 次は品種間に空白を tab で入れましょうか. */ sosen2(A):-rr(A,X,_),write(X),tab(1),sosen2(X). /* ?-sosen2(kosihikari). と入力して実行すると, norin22 norin8 aikoku で,農林22号 の母は 農林8号,その母は 愛国,と出力される. tab(1) の 1 を 2 に変えるともっと空白ができると思うでしょう? してみて下さい. 理屈はともかくそういう直感や試行がこの際大切です. さて,これだけでもスゴイのですが, 4) やはり両親が欲しい? なら次は,以下のように親つまり母と父を定義する. まず母,その母…と探し,なくなったら戻って父,その母,母… と. nl は改行で見やすくするため. */ sosen3(A):-oya(A,X),write(X),sosen3(X). oya(X,Y):-tab(1),haha(X,Y). oya(X,Y):-titi(X,Y). haha(A,X):-rr(A,X,_). titi(A,X):-rr(A,_,X),nl. /* ?-sosen3(kosihikari). と入力して実行すると, norin22 norin8 aikoku asahi norin6 joshu kiryoyosi norin1 oba rikuu132 aikoku kamenoo が出力結果です.改行後に必要な空白を入れたとして見て下さい.つまり, 1行目の 愛国 の下まで2行目の 旭を,農林8号 の下まで3行目の 農林6号 上州 を右に移動・・・; norin22 norin8 aikoku asahi norin6 joshu kiryoyosi norin1 oba rikuu132 aikoku kamenoo で,家系図になってるでしょうが(縦線は勘弁). どうですスゴイでしょう? このたった 5行のプログラムで家系図が描ける こと,FORTRAN(古いなぁ)や Basic 世代の 私には衝撃的でした. 空白をどう入れるかはともかくとして・・・ 5) 次に,近縁係数計算のための世代遡る数は以下(とりあえず母だけ). ここで clock や count はカウンター,retract は消す assert は追加のための組込み関数です. */ sosen4(A):-rr(A,X,_),write(X),count(N),tab(1),write(N),tab(1),sosen4(X). clock(0). count(N):-clock(M),N is M+1,retract(clock(M)),assert(clock(N)). /* ?-sosen4(kosihikari). と入力して実行すると, norin22 1 norin8 2 aikoku 3 で,最後の数字が家系図の改行後空白に利用できそう. 6) 次に,遡るたびの 1/2 乗算は以下. */ sosen5(A): -rr(A,X,_),write(X),count(N),tab(1),write(N),Y is 0.5 ^ N,tab(1),write(Y),tab(1),sosen5(X). /* ?-sosen5(kosihikari). と入力して実行すると, norin22 1 0.500000000000000 norin8 2 0.250000000000000 aikoku 3 0.125000000000000 となり,これでなんとか近縁係数計算への光明が見えます. 7) 共通祖先の検索は以下です. この部分は佐々木昭博さん作成. p_srch で祖先へ,o_srch で子孫へとたどり,その経過は keiro で表示してます. こうなると直感は無理かもだが,この 11行だけでできるということです. not とか fail は 'おまじない' だから気にしないで. */ sosen6(X,Y):-not(o_srch(X,Y,[[],[X]])),not(o_srch(Y,X,[[],[Y]])),not(p_srch(X,Y,[X])). p_srch(X,Y,L):-(rr(X,V,_);rr(X,_,V)),append(L,V,L1),not(o_srch(V,Y,[L1,[V]])), not(p_srch(V,Y,L1)),fail. o_srch(V,Y,[L1,L2]):-(rr(Y,V,_);rr(Y,_,V)),append(L2,Y,L3),keiro(L1,L3),!,fail. o_srch(V,Y,[L1,L2]):-(rr(C,V,_);rr(C,_,V)),not(member(C,L1)),append(L2,C,L3), not(o_srch(C,Y,[L1,L3])),fail. keiro(L1,L2):-write('* '),l_disp(L1),nl,write('** '),l_disp(L2),nl. l_disp([]):-!. l_disp([H|B]):-write(H),write(' '),l_disp(B). not(X):-X,!,fail. not(_). append([],X,[X]). append([A|X],Y,[A|Z]):-append(X,Y,Z). /* 実行してみましょう. ?-sosen6(norin21,norin22). と入力すると, * norin21 asahi ** asahi norin8 norin22 * norin21 norin1 rikuu132 aikoku ** aikoku norin8 norin22 と農林21号 と 22号 の共通祖先である 旭 と 愛国 が検索されます. 稲の 全データ を入れて,例えば domannaka,kosihikari 間の検索をしてみて下さい. この両者間の近縁係数計算をすると全経路数 86 検索をします. その出力ファイル. どまんなか,キヌヒカリ では 586,ななつぼし,あさひの夢 ではなんと 5784 の経路です. Prolog が勝手に計算してくれるのを見ていると感動します. また手続き型言語では到底無理だとも実感します. 以上が家系図作成や近縁係数算出の主要部分です. 8) あとはエラー防止(不都合防止のためダミー両親挿入など), 時間短縮のため必要データ のみ抽出, 家系図を整えるため長さの異なる品種名の調整, カウンタ設定とその初期化, 近縁・近交係数の Σ計算, 共通祖先の近交係数読み込み, 総当たり交配, そのファイル 出力 (Web 版はナシ), 漢字処理, 祖先や世代数計算 などを試行錯誤で加えています. 各種の状況下で多量の計算をして確認したのでバグは多分ないはず. 9) 以下は両親名データ. rr('コシヒカリ','農林22号','農林1号'). と漢字もOK. */ rr('kosihikari','norin22','norin1'). rr('norin22','norin8','norin6'). rr('norin8','aikoku','asahi'). rr('norin6','joshu','kiryoyosi'). rr('norin1','oba','rikuu132'). rr('rikuu132','aikoku','kamenoo'). rr('norin21','asahi','norin1').