descartes-src (ソースパッケージ descartes-src-0.26.0.tar.gz) | 2012-09-09 20:57 |
descartes-win (Windows用バイナリパッケージ descartes-win-0.26.0.zip) | 2012-09-09 20:52 |
会話キャラクター: ツンデレ アプリケーション (会話キャラ:ツンデレ v1.0 for Windows) | 2010-04-29 13:41 |
会話キャラクター: 2人の女の子 ダブルキャラクター (会話キャラクター 2人の女の子 ダブルキャラクター 1.0 for Windows) | 2011-10-02 22:23 |
会話キャラクター: Eliza風英語版 (会話キャラ:Eliza風英語版 v1.0 for Windows) | 2010-05-11 01:06 |
会話キャラクター: 猫耳メイド アプリケーション (会話キャラ:猫耳メイド v1.0 for Windows) | 2010-04-27 21:15 |
会話キャラクター: イライザ風日本語版 (会話キャラ:イライザ風日本語版 v1.0 for Windows) | 2010-04-30 21:53 |
経済指標表示プログラム for Windows (経済指標表示プログラム V1.0) | 2011-08-18 22:04 |
ニュースヘッドライン表示プログラム (ニュースヘッドライン表示プログラム V1.0 for Windows) | 2011-08-16 12:31 |
デカルト言語 example (デカルト言語の例題 example-0.7.0.zip) | 2009-03-01 19:47 |
電力状況表示プログラム for Windows (2011年夏版 全国電力供給状況表示プログラム V1.0) | 2011-08-15 13:25 |
チェスのQueenは、縦横ななめ、どの方向にもまっすぐに進んでいけるオールマイティな至高な存在です。
そのような誇り高きQueenを、8x8のチェス盤に8人置いて、かつ互いに進んでいける進路上で干渉しないように配置するのが 古来よりある8Queenパズルであります。
上のチェス盤に置いてある8つのQueenで確かめてみてください。 お互いの利き筋が重複していないことが確認できるはずです。
このような、Queenの配置を解決するプログラムをデカルト言語で解いてみましょう。 プログラムのソースを以下に示します。
<qprint #N> <for (#i #N) <for (#j #N) (<q #j #i> <printf "Q "> | <printf "_ ">) > <print> > <print> ; <check #x #y> <for (#i #y) <q #j #i> <compare #j != #x> <compare #i-#j != #y-#x> <compare #i+#j != #y+#x> > ; <nqueen #y #N> <compare #y >= #N> <qprint #N> <print> ; <nqueen #y #N> <#y1 = #y + 1> <for (#x #N) [ <check #x #y> <setArray q #x #y> <nqueen #y1 #N> ] > ; ? <nqueen 0 8>;
以下のように考えて解きます。
1) 1行には必ず1つのQueeenしかないので、0行目から順にQueenを0桁目から入れて試す。
2) 新しく設定したQueenが行が縦と斜めの方向で衝突しないかチェックして、衝突していたら次の位置を探す。
3) 最後の行までQueenの位置が決定したら、結果を表示する。
4) 異なる解がないか探索するため1)を別の位置の桁から試す。
5) すべての解を出力したら終了する。
まず、最初のqprint述語は、8QUEENの盤面を表示する述語の定義です。
<qprint #N> <for (#i #N) <for (#j #N) (<q #j #i> <printf "Q "> | <printf "_ ">) > <print> > <print> ;
2重のforループですから簡単ですね。
ポイントは、ループの中です。
(<q #j #i> <printf "Q "> | <printf "_ ">)
<q #j #i>によって、配列qのインデックス(#j, #i)に値が設定されていれば、そこにはqueenがいると判断して "Q "を表示します。 それ以外ならば、空欄として"_ "を表示します。 その処理の切り替えの判定を"|"(論理和)によって選択しています。 "|"の前の処理が成功すれば、その後の処理は実行されません。 失敗すれば、後ろの処理が実行されます。
この処理で2重ループの中で盤面全体をスキャンしながら、結果の表示を行うのです。
次のcheck述語は、引数の(#x,#y)の位置にqueenを置いても他のqueenと衝突しないかチェックするものです。
<check #x #y> <for (#i #y) <q #j #i> <compare #j != #x> <compare #i-#j != #y-#x> <compare #i+#j != #y+#x> > ;
最初のfor述語により、0から#y-1までループさせます。 これは、0行目から順にqueenを置いていくため、現在queeenを置く位置を探っている#yより後ろにはqueenはないため、この範囲で十分なのです。
次の<q #j #i>で、#i行目にあるqueenを見つけ出します。
<compare #j != #x>は、見つけ出したqueenが、#xと同じ列にないかをチェックします。 これは、縦方向のチェックに相当します。
斜め方向については、次のように考えます。
チェス盤をx、yの座標と考えると、斜め方向の方程式は傾きが1および-1の以下のような形になります。
y = x + a y = -x + b
そこで、同じ斜め方向にあるということは、切片aやbが一致する場合です。
a = y - x b = y + x
よって、y-xとy+xの値が一致する場合には、斜め方向でqueenが衝突すると判定します。 その判定がcheck述語の中の以下の判定で行われます。
<compare #i-#j != #y-#x> <compare #i+#j != #y+#x>
次のnqueen述語は、2つ定義があります。 これは、最初の定義が成功すれば、それを実行し、 失敗すれば2つ目の定義を実行するように動作します。
<nqueen #y #N> <compare #y >= #N> <qprint #N> <print> ; <nqueen #y #N> <#y1 = #y + 1> <for (#x #N) [ <check #x #y> <setArray q #x #y> <nqueen #y1 #N> ] > ;
最初のnqueen定義は、まず、<compare #y >= #N>で引数#yが#Nを超えている場合には解が求められたと判定して 先に説明したqprint述語で結果を表示します。
ちなみに8queenではなく、nqueenなのは、8以外の盤でも解が求められるようにするために、盤の幅を#Nで指定できるようにするためです。
次のnqueenの定義が実際の解を求める処理を再帰的に行っているものです。
check述語で、(#x, #y)にqueenを置いて問題ないかチェックし、 問題なければ組み込み述語のsetArrayで配列qの(#x, #y)のインデックスを設定します。
その後、#yに1を足した座標で、nqueen述語を再帰的に呼び出しています。
最後の行で、nqueen述語を呼び出して、プログラムの実行を行います。
? <nqueen 0 8>;
引数の0は、0行目から開始するということです。
引数の8は、8queenを調べることを指定しています。 たとえば、この値が10になれば、10x10の盤で10個のqueenを配置する解を求めます。
どうでしょうか?
以外に簡単ですね。デカルト言語は論理言語としての特徴を持つのですが、 手続き的なプログラムも同様に間単に書けます。
つまり、デカルト言語では論理的な処理と手続き的な処理を融合させて 書きやすい方法でプログラムを記述することができるようになっているのです。
[PageInfo]
LastUpdate: 2009-12-20 21:17:52, ModifiedBy: hniwa
[License]
Creative Commons 2.1 Attribution
[Permissions]
view:all, edit:login users, delete/config:login users