基于上面的评论和你仍然是 Prolog 初学者的事实,我将尝试解释一些可能不太清楚的事情:
What are variables in Prolog and what is instantiation ?
在 Prolog 中,变量是以大写字母和匿名变量开头的_
(只是下划线定义了一个匿名变量,它是一个没有名称的变量)。Prolog 中的变量意味着它们可以表示任何东西,一个列表、一个字符串、一个原子(......无论如何),当你使用一个新变量时,它不会被实例化,这意味着它可以表示任何东西,但现在它确实表示 - 它没有'不是bind
列表,原子......当你想实例化一个变量时,你可以这样做:
- 通过使用统一
=/2
,例如X = 2
统一实例化变量 X 与数字 2。
- 或仅使用类似上述
is/2
的表达式。X is 2
非常重要的是,当 Prolog 中的一个变量被实例化时,我们知道它代表什么并且我们不能改变例如X =2, X = 3 will fail!!
,因为我们知道 X 与 2 绑定并且我们尝试用 3 重新实例化失败。
What is =/2, is/2, and what's the difference between these two prediactes ??
is/2
:它将算术 表达式的结果返回给未实例化的变量。这意味着您可以像这样使用它:
X is 2, or X is mod(Z,Y)
但重要的是要注意这里的任何内容,expression MUST be instantiated
如果它是以前的 Y 和 Z 之类的变量。如果表达式未实例化,您将获得instantiation error
与您的情况相同的结果。如果 X 没有被实例化,即使是简单的事情2 is X+1
也会因实例化错误而失败,所以不要指望 is/2 会返回类似于X=1
前一种情况的东西,这是一个明显的答案(但不是唯一的,所以它会不一致!)。算术表达式也意味着你不能使用类似的东西X is []
, 将 X 与空列表统一这当然会失败,所以为了做到这一点=/2
,如下所述。
=/2
:这用于将变量(或一般而言的术语)与另一个术语统一。例如,您可以像这样使用它:X = 2
将 X 与数字 2X = [1, 2]
统一,或者将 X 与一个列表统一,或者X = 1 + 2
不会给出 3 而只是术语 1 + 2,我认为最后两个示例清楚地显示了与is/2
.
But then how could we use arithmetic expressions like is/2 but more relational ?
这里的答案是library CLPFD
(正如@lurker 在评论中所推荐的那样)您所要做的就是将其:- use_module(library(clpfd)).
放入您的文件中并使用运算符#=
而不是is/2
. 现在您可以尝试X #= 2
,它将用数字 2 或什至实例化变量 X, 2 #= 1 + X
现在您没有收到实例化错误,但您得到了答案X = 1
,因此您可以看到它现在更具相关性。
现在问题...
我同意@Boris 中性不应该通过递归进行,而是用函数声明。虽然我会尽量跟上你的回答尝试:
首先,当尝试在 swi prolog 中使用您的代码查询文件时,我得到:
Warning: /Users/Desktop/reduce.pl:1:
Singleton variables: [F,NEUTRAL,EE]
这表示变量 F,Neutral,EE 在这个子句中是单例的:
reduce([],F,NEUTRAL,EE).
如您所见,您没有在此子句中的任何地方使用,例如变量 F。
在我看来,reduce 谓词应该因空列表而失败,因为您不能对中性元素和带有两个参数的函数进行任何操作。因此,作为基本情况,我将使用只有一个元素的列表来定义:
reduce([H],F,NEUTRAL,EE):-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal).
然后如上所述,规则如下:
add(N1,NEUTRAL, EE) :- EE is EE + N1 + NEUTRAL.
将导致实例化错误,因为在 EE 中是 var 并且您不能在 is/2 中使用它,但即使它已被实例化,您也无法使用它,因为正如所解释的 EE is EE + whatever
那样不起作用,因为you try to reinstantiate a variable..!!
为了修复它,我会尝试使用另一个变量进行下一次递归,然后计算结果如下:
reduce([H],F,NEUTRAL,EE):-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal).
reduce([H|T],F,NEUTRAL,EE) :-
reduce(T,F,NEUTRAL,EE1),
Goal =.. [F, H, EE1, EE],
call(Goal).
(这里添加了一个新变量 EE1 进行递归调用,然后根据递归调用返回的 EE1 的 Result 计算结果。)
现在让我们测试一下:
?- reduce([1,2,3,4],add,0,L).
L = 10 ;
false.
?- reduce([1,2,3,4],mult,1,L).
L = 24 ;
false.
L=10
当它通过按“;”给你结果时,还有一件事 我们要求更多答案并返回 false,因为没有可能的答案。错误或真实的事情只是 Prolog 的顶层通知您谓词成功或失败的一种方式。例如:
?- reduce([1,2,3,4],mult,1,24).
true ;
false.
这表明上述情况是正确的,当我们询问是否有任何其他方式可能是真的时,它返回错误......就是这样,毕竟Prolog似乎没有那么糟糕:)(根据你的描述)。
作为一个练习,您可以尝试使用 CLPFD 编写相同的代码,以便更具关系性并能够回答以下查询:reduce(L,mult,1,24).
也许@Boris 的想法是不通过递归一直携带中性。
编辑
根据@false 的建议和有用的评论,我将使用 call/N 编写第二种方式,这显然更好(见评论...):
reduce([H],F,NEUTRAL,EE):-
call(F, H, NEUTRAL, EE).
reduce([H|T],F,NEUTRAL,EE) :-
reduce(T,F,NEUTRAL,EE1),
call(F, H, EE1, EE).