web-dev-qa-db-ja.com

プロローグ-引数が十分にインスタンス化されていません

リスト内の要素が数字ではない数を数える小さなプログラムを書いています。ここに私のコードがあります:

not_number([],0).
not_number([X|T],R):- 
    not(number(X)),
    R1 is R+1,  
    not_number(T,R1). 

not_number([_|Tail],Result):-
    not_number(Tail,Result).  

このようなコードを実行すると:

?- not_number([1,2,3,5], R).

私はR = 0になっています(あるはずです)

R = 0.

しかし、リストにキャラクターを入れた場合:

?- not_number([1,2,3,5,a], R).

その後、私はこのエラーを取得しています:

ERROR: not_number/2: Arguments are not sufficiently instantiated
   Exception: (10) not_number([a], _G247) ? 

誰かがコードの問題を説明できますか?私はプロローグが初めてです。

18
Eddwhis

lurker による comment にまだ最良の答えがあったため、私はこの答えを書いています。実際の回答として表示してもらいたいです。

not_number([X|T], R)の場合にRがインスタンス化されていないときにR1 is R+1を実行しているため、コードは機能していません。あなたの再帰的なケースは少し後方に張り巡らされています。これを行いたい:

not_number([X|T],R):- 
    not(number(X)),
    not_number(T,R1),
    R is R1+1.

これで、isの右側が呼び出されたときにインスタンス化されます。

18
Rialgar

あなたの問題は、このような算術計算では:

AはB

右側(B)のすべてが既知である必要があります。変数はありません。

次のようなことができます:

not_number(X, Y) :- not_number(X, Y, 0).
not_number([], Y, Y).
not_number([H|T], Y, Z) :-
    \+ (number(H)), 
    Z1 is Z+1,
    not_number(T, Y, Z1).

not_number([H|T], Y, Z) :-
    number(H),
    not_number(T, Y, Z).

(このコードをテストし、動作します)。

ここで、3番目の引数はアキュムレーターです。非数がいくつあるかをカウントします。リストが空の場合、この3番目の引数は2番目の引数と統合され、適切な答えになります。

プロローグは、機会があれば、可能なすべてのルートを通過します。このようなことをする場合:

cat(adam).
cat(eve).

そして尋ねる:

?- cat(X).

X = adamとX = eveの両方の答えを得ることができます。これはコードにも適用されます。リストの先頭が数字でない場合でも、これを行うことができます。

not_number([_|Tail],Result):-
    not_number(Tail,Result).  

希望する答えは得られません。興味のないルートを遮断する必要があります。この場合、私は追加します

number(Head).

この要素が数値でない場合にのみ、カウンタを1増やすことなくリスト内の要素をスキップするようにします。

Prologが他の結果を見つけるように強制するには、「;」を押す必要があります。キーボードで(このadamとeveの例のように)。

4
Darge Elar

このような問題の一般的な解決策は、constraintsを使用することです。

たとえば、 clpfd 制約のみを使用する場合、プログラムは期待どおりに機能します。 (is)/2(#=)/2に置き換えるだけで、すべての方向で機能する整数演算を取得できます

:- use_module(library(clpfd)).

not_number([],0).
not_number([X|T],R):- 
    \+ number(X),
    R1 #= R+1,  
    not_number(T,R1). 

not_number([_|Tail],Result):-
    not_number(Tail,Result).

サンプルクエリとその結果:

?-not_number([1,2,3,5]、R)。
R = 0。

また、(\+)/1の代わりにISO述語not/1を使用するようにコードを変更したことにも注意してください。

0
mat