1

私はプロローグを学んでいて、リスト内の特定の要素の出現を数えたいと思っています。

だからここにコードがあります -

count(_, [], _) := !.

count(El, [El|T], N) :-
    N1 is N + 1,
    count(El, T, N1).

count(El, [_|T], N) :-
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M is N.

したがって、基本的にはコンソール check([3,4,3], [2,3,4,5,2]) に渡したいのですが、list1 での 3 の出現は次の出現と同じであるため、true を返す必要があります。リスト2の2。しかし、代わりにそれは私を投げます -

Arguments are not sufficiently instantiated. 
Exception: (10) _G1383 is _L155+1 ? creep
Exception: (9) count(3, [3, 4, 2], _G1385) ? creep
Exception: (8) count(3, [2, 3, 4, 2], _G1385) ? creep
Exception: (7) check([2, 3, 4, 2], [2, 3, 4]) ? creep 

これは何が原因で、どうすれば解決できますか? フォーラム全体をチェックしましたが、どこでもこれが機能するはずだと書かれています。これはある種のバージョン関連のものですか、それとも本当にここに何か欠けていますか?

編集: SWI-Prologを使用します。

EDIT2:

うまくいきました、ありがとう!

コード:

count(_, [], 0) :- !.

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1.

count(El, [Y|T], N) :-
    El \= Y,
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M #= N.
4

2 に答える 2

2

宣言型演算を使用することで、これが機能するようになったことは素晴らしいことです。

あなたが得た解決策について、いくつかの小さな追加コメントがあります。

count(_, [], 0) :- !.

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1。

count(El, [Y|T], N) :-
    El \= Y,
    count(El, T, N)。

チェック (リスト 1、リスト 2) :-
    カウント(3、リスト1、M)、
    カウント(2、リスト2、N)、
    M #= N.

最初に、check/2はコードのどこにも使用されていないことに注意してください。そのため、以下ではその定義を省略します。

最も一般的なクエリ

私が何も知らない Prolog コードを確認するときは、すべての引数が変数である最も一般的なクエリを常に最初に試します。理想的には、答えは一般的な解決策を示してくれます。

たとえば、あなたの場合:

?- カウント (E、Ls、C)。
Ls = []、
C = 0 .

これは、誤って、述語の解が1 つしかないことを示しています。これは明らかに意図したものではありません。

したがって、最初のステップとして、 を削除して!/0、このコードを次のように変更することをお勧めします。

カウント (_、[]、0)。

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1。

count(El, [Y|T], N) :-
    El \= Y,
    count(El, T, N)。

この変更を適用すると、次のようになります。

?- カウント (E、Ls、C)。
Ls = []、
C = 0;
Ls = [E]、
C = 1;
Ls = [E, E],
C = 2;
Ls = [E, E, E],
C = 3 .

これははるかに良く見えます。現在、いくつかの有効な答えが得られています。

終了

この述語からいくつの答えが得られるでしょうか? 特に、答えは有限個しかないのですか?その場合、次のクエリが終了すると予想されます。

?- count(E, Ls, C), false .

試してみて、実際に終了しないことを確認してください(少なくともすぐには終了しません)。の正しい実装から、最も一般的なケースで非終了が期待されるため、これは良い兆候です!count/3

完全

理想的には、述語が完全であることを望みます:有効な答えを省略すべきではありません。

例えば:

?- count(E, [X,Y,Z], C)。
E = X、X = Y、Y = Z、
C = 3 ;
間違い。

これらは本当にすべてのソリューションですか?私はそうは思わない!確かに とは異なる長さ 3 のリストがあります [E,E,E]

実際、あなたのプログラムはある意味でそれらについても「知っています」:

?- count(E, [1,2,3], C)。
E = C、C = 1 ;
間違い。

しかし、繰り返しになりますが、これらはすべてのケースではありません。E1と異なる答えはどこにありますか?

非単調 (\=)/2 述語を使用しているため、これらの問題に直面しています。これには、特に現在 Prolog を学習し始めたばかりの場合、理解するのが非常に難しいプロパティがいくつかあります。例えば:

?- X \= Y、XY = ab.
。

?- XY = ab、X \= Y.
X = a、
Y = b .

次のバージョンを取得して、2 つの用語が異なることを示すために代わりに使用するdif/2ことをお勧めします。

カウント (_、[]、0)。

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1。

count(El, [Y|T], N) :-
    dif(El, Y),
    count(El, T, N)。

このバージョンでは、次のようになります。

?- count(E, [X,Y,Z], C)。
E = X、X = Y、Y = Z、
C = 3;
E = X、X = Y、
C = 2、
差分 (Y, Z) ;
E = X、X = Z、
C = 2、
差(Z、Y) ;
等

そして、特に:

?- count(E, [1,2,3], C)。
E = C、C = 1 ;
E = 2、
C = 1;
E = 3、
C = 1;
C = 0、
差分(E, 3),
差分(E, 2),
dif(E, 1) .

これにより、発生する可能性のあるすべてのケースがカバーされます。

公正な列挙

述語は純粋単調なので、反復的な深化によって答えを公平に列挙するために使用できます。

例えば:

?- 長さ (Ls、_)、カウント (E、Ls、C)。
Ls = []、
C = 0;
Ls = [E]、
C = 1;
Ls = [_G588]、
C = 0、
dif(E, _G588) ;
Ls = [E, E],
C = 2;
Ls = [E, _G597],
C = 1、
dif(E, _G597) .
C = 2 ;
等

これはとてもいいことで、これを真の関係として、カウントだけでなく、生成にも使用できることを示しています。

したがって、この述語が一般的に何を意味するかをより適切に説明する述語を検討することができます。これは演習として残します。

末尾再帰バージョン

純粋な述語を使用しているため、目標を自由に並べ替えて、述語の末尾を recursiveにすることができ、次の結果が得られることに注意してください。

カウント (_、[]、0)。
count(El, [El|T], N) :-
    N #= N1 + 1、
    count(El, T, N1).
count(El, [Y|T], N) :-
    dif(El, Y),
    count(El, T, N)。

決定論

現在、たとえば次のようなものがあります。

?- count(a, [a,a,a], Cs)。
Cs = 3 ; 

を使用すると、この述語の決定if_/3性を改善できます。

:- use_module(ライブラリ (reif) )。

カウント (_、[]、0)。
count(E, [L|Ls], N) :-
        if_ (E=L, N #= N1 + 1, N #= N1),
        カウント (E、Ls、N1)。

これにより、述語は少なくともインデックス作成に適したものになります。このような場合に決定論を改善するかどうかは、Prolog システムのインデックス作成メカニズムによって異なります。

于 2016-11-19T09:20:00.100 に答える
1

非常に特殊な状況でしか使用できないため、モード化と呼ばれる述語を使用しています。特に、(is)/2ここで必要なため、関係として使用することはできません。

これを解決する 1 つの方法は、より一般的な述語を代わりに使用することです。たとえば、整数を推論する場合、Prolog システムのCLP(FD) 制約を使用することを検討してください。これは、すべての方向で機能します。

たとえば、GNU Prolog では、次のように置き換える (is)/2だけでエラーを解消できます(#=)/2

カウント(_、 []、 _)。

count(El, [El|T], N) :-
    N1 #= N + 1、
    count(El, T, N1).

count(El, [_|T], N) :-
    count(El, T, N)。

チェック (リスト 1、リスト 2) :-
    カウント(3、リスト1、M)、
    カウント(2、リスト2、N)、
    M #= N.

今、私たちは得ます:

?- count(3, [3, 4, 2], C)。
C+1#=_1204 ;
真実 ;
偽

(または、Prolog システムによっては、同等の回答)。

なぜですか?明らかに、プログラムには少し欠陥があります。

間違い探しは練習問題として残しておきます。ヒント:M #= N怪しいと思われます: これは、 ...と等しい場合に当てはまります。 M N

于 2016-11-17T12:03:06.400 に答える