質問1:
Prologシステムは、条項を実行する前に、条項が適用されるかどうかを常に判断できるとは限りません。正確な状況は実装に依存します。つまり、一般的にその決定に頼ることはできません。システムはここでリリースごとに改善されます。最も単純なケースとして考えてみましょう。
?-X = 1; 1=2。
X = 1;
false。
非常に賢いPrologは、それが1 = 2
常に失敗することを検出できるため、代わりに単に答えることができますX = 1.
。一方、このような「賢さ」は実装に非常にコストがかかり、より頻繁なケースを最適化するためにより多くの時間が費やされます。
では、なぜプロローグはこれをまったく示していないのでしょうか?主な理由は、Prologがそれ以上の答えがないことをすでに知っている場合、別の答えを素直に尋ねることを避けることです。したがって、この改善の前に、変数を含むすべてのクエリに対して別の回答を求められ、1つの回答false
だけですべてのクエリに「いいえ」が表示されました。これは以前は非常に面倒だったため、多くのプログラマーは次の答えを求めなかったため、意図しない答えについて警告を受けませんでした。
そして二次的な理由は、実装の制限を認識し続けることです。Prologがこの一般的なクエリで別の答えを求めた場合、これは、すべてのコンピューティングリソースを蓄積して消費する可能性のあるスペースをまだ使用していることを意味します。
あなたの例でlast1/2
は、そのようなケースに遭遇します。そして、あなたはすでに非常に賢いことをしました、ところで:あなたは予期しない振る舞いの最初の発生を見るためにクエリを最小化しようとしました。
あなたの例のクエリlast1([1,2],X)
では、Prologシステムはリスト全体を見るのではなく[1,2]
、主要なファンクターだけを見る。last1([_|_],X)
したがって、Prologシステムの場合、クエリは、適用する句を決定するときと同じように見えます。この目標は現在両方の条項に当てはまります。これが、Prologが試してみる代わりに2番目の条項を覚えている理由です。
しかし、考えてみてください。この選択は、最後の要素を除くすべての要素で可能になりました。つまり、要素ごとにいくらかのメモリを支払うということです。非常に長いリストを使用することで、実際にこれを観察できます。これは私の小さな32ビットラップトップに搭載されています—より大きなシステムではさらに0または2を追加する必要があるかもしれません:
?-長さ(L、10000000)、last1(L、E)。
エラー:ローカルスタックが不足しています
一方、事前定義last/2
はスムーズに機能します。
?-長さ(L、10000000)、last(L、E)。
L = [_G343、_G346、_G349、_G352、_G355、_G358、_G361、_G364、_G367|...]。
実際、それは一定のスペースを使用します!
これには2つの方法があります。
定義を最適化してみてください。はい、あなたはこれを行うことができますが、あなたは非常に賢くなければなりません!たとえば、@back_dragonによる定義は正しくありません。初心者が実際にそのセマンティクスを破壊しているときに、プログラムを最適化しようとすることがよくあります。
と同じ述語を実際に定義しているかどうかを自問してくださいlast/2
。実際、あなたはそうではありません。
質問2:
検討:
?-last(Xs、X)。
Xs = [X];
Xs = [_G299、X];
Xs = [_G299、_G302、X];
Xs = [_G299、_G302、_G305、X];
Xs = [_G299、_G302、_G305、_G308、X]
..。
と
?-last1(Xs、X)。
**ループ**
したがって、この場合の定義はSWIの定義とは異なります。句の順序を交換します。
?-長さ(L、10000000)、last2(L、E)。
L = [_G343、_G346、_G349、_G352、_G355、_G358、_G361、_G364、_G367 | ...];
false。
繰り返しますが、これfalse
!しかし今回は、大きなリストが機能します。今回は、最小限のクエリは次のとおりです。
?-last2([1]、E)。
E = 1;
false。
そして状況は非常に似ています:繰り返しますが、Prologはクエリを同じように見て、last2([_|_],E)
両方の条項が適用されると結論付けます。少なくとも、線形オーバーヘッドではなく一定のオーバーヘッドがあります。
このオーバーヘッドをクリーンな方法で克服する方法はいくつかありますが、それらはすべて実装の内部に大きく依存しています。