Prolog - 参数未充分实例化

32
我正在写一个小程序,用于计算列表中有多少个元素不是数字。以下是我的代码:
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) ? 

有人能解释一下这段代码有什么问题吗?我是Prolog的新手。


16
not_number([a], R)这种情况下,你执行了R1 is R+1,但是R并没有被实例化。你的递归情况有点颠倒了。你应该执行not_number(T, R1), R is R1+1 - lurker
这个页面还有关于is的好讨论。 - Yibo Yang
4个回答

35
我是一名有用的助手,可以翻译文本。

我写这个答案,因为迄今为止最好的答案在评论中由lurker提供。我希望它能显示为一个实际的答案。

你的代码没有起作用,因为在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时,其右侧被实例化。


6

您的问题在于这样的算术计算:

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).

我已经测试了这段代码,它有效。

现在第三个参数是一个累加器。它计算有多少非数字。当列表为空时,第三个参数与第二个参数合并,并成为正确的答案。

如果给予机会,Prolog会尝试遍历所有可能的路径。如果你做了这样的事情:

cat(adam).
cat(eve).

然后询问:
?- cat(X).

你可以得到两个答案:X = adam 和 X = eve。这同样适用于你的代码:请注意,当列表的头部不是一个数字时,你仍然可以这样做:
not_number([_|Tail],Result):-
    not_number(Tail,Result).  

这并不是你想要的答案,你需要剔除那些不感兴趣的路线。在这种情况下,我会添加

number(Head).

为了确保我们在列表中跳过一个元素而不增加计数器1,只有当这个元素不是数字时才能实现。

为了强制Prolog查找其他结果,您必须在键盘上按“;”(就像在这个亚当和夏娃的例子中一样)。


4
这���问题的通用解决方案是使用约束
例如,如果你只使用约束,程序将按预期工作。只需用(#=)/2替换(is)/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。

请注意,我已更改代码以使用ISO谓词(\+)/1而不是not/1


?- not_number([A,A,B],N). 有一个解,N = -3。这看起来不对。 - repeat
1
罪魁祸首?当然是 number/1,因为它混淆了实例化测试和类型测试。解决方法:使用 functor(X,_,_), \+ number(X) 代替 \+ number(X)。或者,更好的方法是使用正确的具体化测试谓词的 if_/3... - repeat
非常正确。我建议使用 must_be(nonvar, X)。现在可以讨论这个额外的点,因为通过使用声明性算术,OP报告的根本问题已经得到解决。 - mat
对,我错过了实际要点,因为我没有注意到这两个代码有多么相似。 - repeat

0
在我的情况下,我不得不使用=而不是is
而不是这样。
X is AnotherVariable

我必须写

X = AnotherVariable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接