@Junuxxの答えは解決策への一歩です; プログラムにも別の問題があります。しかし、最初の一歩後退:@Junuxxは問題を発見し、修正しました。良い。しかし、どうすればそのような問題を見つけることができますか?実際、あなたは»無限ループを尋ねましたが、どうやって?«
Prologの優れている点は、ループしているプログラムをプログラムの非常に小さな断片にローカライズできることです。このようなフラグメントは、失敗スライスと呼ばれます。つまり、長いプログラムを読むのに目が痛くなることはもうありません。
プログラムに戻りましょう。ロードすると、次のようなメッセージが表示されます。
警告:/usager/SO/paranoid.pl:13:
シングルトン変数:[結果]
これは、おそらく間違っていることについてのヒントをすでに与えています。残念ながら、これは今のところ最大の懸念事項ではありません。あなたの最大の問題は、ゴールtest
がループすることです!
非終了のローカライズ
では、どのようにして少ない労力で実際にループしているものを理解できるでしょうか。
1つの方法は、トレーサーを起動することです。これにより、Prologがこのプログラムを実行する方法が段階的に示されます。ただし、トレーサーは無関係な詳細を多く表示します。詳細、Prologでプログラミングするときに理解する必要はありません。詳細、それはあなたが実際の問題を完全に見逃す可能性があるようにあなたの心を満たします。したがって、線がちらつく画面で時間を過ごしたい場合を除いて、トレーサーには近づかないでください。
もう1つの方法は、プログラムに目標を追加することfalse
です。あなたのプログラムはすでにループしているので、そのような余分な目標はあなたをそれほど傷つけないことを覚えておいてください。false
そもそも書きたくなかったこれらの目標でプログラムを破壊するのはなぜですか?これらのfalse
目標は、「無関係な」部分を非表示にすることで、プログラムの非終了の原因を検出するのに役立つためです。これは、次の観察のおかげでそうです:
失敗スライス(=破壊されたプログラム)が終了しない場合、元のプログラムも終了しません。
ある意味で、フェイルスライスはプログラムが終了しない理由です。または、もっと強く言えば、失敗スライスで表示されている部分を変更しない限り、つまり、失敗スライスに表示されていない部分を変更することによって運を試しているだけである限り、問題は解決しません。保証!それは最高の保証ではありませんが、盲目になるよりはましです。
これが私が失敗スライスとして得たものです。printSentence/1
フラグメントで使用されなくなったため、削除しました。そして、の定義を追加しましたappend/3
。一部のPrologはappend/3
、変更できない組み込み述語として提供されます。その場合は、次のような別の名前を使用local_append/3
してください。すべてのオカレンスを置き換えることを忘れないでください。
append([]、Zs、Zs):- false。
append([X | Xs]、Ys、[X | Zs]):-
append(Xs、Ys、Zs)、false。
transform([]、Result):- false。
transform([Word | Rest]、Result):-
replace(Word、Replacement)、
append(Result、Replacement、NewResult)、false、
transform(Rest、NewResult)。
replace(my、your):- false。
replace(i、you):- false。
置換(あなた、私)。
replace(am、are):- false。
replace(Word、Word):- false。
テスト :-
X = [あなたは、私の、唯一の、希望]、
transform(X、Result)、false、
printSentence(Result)。
この失敗スライスをロードすると、次のようになります。
?- テスト。
エラー:ローカルスタックが不足しています
これは、プログラムが終了しないことを示す良い兆候です。私の有限のハードウェアでは、代わりにすべてのリソースを使い果たします。((衒学的には、このプログラムはまだ終了する可能性があり、必要なリソースが多すぎる可能性があります。ただし、これは、障害スライスがループし、次にプログラム全体がループする場合に発生します。いずれの場合も、障害が終了しないことを証明します-フラグメントが短いため、スライスが簡単になることがよくあります))。
いくつかの観察:元々、transform/2
再帰的でした。今では、もはやそうではありません。残っている再帰は内にありますappend/3
。それで、私は最初に目標append(Result, Replacement, NewResult)
を見て、変数が何であるかを理解しようとします。最も簡単なのは3番目の引数です。NewResult
フラグメント内で唯一出現するので、これを。に置き換えることができます_
。2番目の引数の変数Replacement
は常にme
。です。そして、最初の引数(ここで私は今見なければtest/0
なりません)はインスタンス化されていない変数になります。したがって、目標を検討する必要がありappend(_, me, _)
ます。
append(_, me, _), false
この目標が終了しないことを確認するために実行するだけです!これは、障害スライスを調べることでも確認できます。これがまたです:
append([]、Zs、Zs):- false。
append([X | Xs]、Ys、[X | Zs]):-
append(Xs、Ys、Zs)、false。
見てくださいYs
:誰もそれを気にしません、それはただ「引き渡される」だけです。最初と3番目の引数だけが終了を保証するかもしれません!
詳細については、タグのfailure-sliceを参照してください。
ファインプリント
特定の制限が適用されます!禁止されている場所では無効になります!上記の推論は、純粋な単調なPrologプログラムでのみ実行できます。実際、あなたがあなたのプログラムに持っているものとしてのいくつかの良性の副作用もOKです。それらが制御フローに影響を与えない限り。
他の問題
プログラムに別の問題があります。それを見るために走っprintSentence([you]), false
てください!バックトラックと副作用は簡単に一緒に群がりません。初心者にとっては、副作用を一斉に回避するのが最善です。プログラミングの問題で役に立たない副作用を取り除く方法の例については、この質問とその回答を参照してください。transform([you, are, my, only hope], Xs)
電話またはmaplist(replace,[you, are, my only, hope], Xs)
直接電話してみませんか?それはあなたが再び関連する部分に集中することを可能にします!