我们目前正致力于谓词reduce的实现,需要列出L, 二元函数F,对L的元素进行操作,并具有中性元素N. 并将它们映射到一个数字E,它是通过递归地将F应用于L的元素而获得的。
到目前为止,这是我们的代码:
reduce([],F,NEUTRAL,EE).
reduce([H|T],F,NEUTRAL,EE) :-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal),
reduce(T,F,NEUTRAL,EE).
add(N1,NEUTRAL, EE) :- EE is EE + N1 + NEUTRAL.
mult(N1,NEUTRAL, EE) :- EE is N1 * EE * NEUTRAL.
问题是:对于?- reduce([], add, 0, R).
等相同的样本查询,我们只得到一个布尔值(在本例中为true),向其他人返回一个错误,告诉我们我们的Arguments没有正确实例化。我们实际的目标是像R = 0
这样的回应。 (对于上面给出的例子)。
希望你能帮助我们,请不要因为我们是Prolog的初学者而太复杂;)
答案 0 :(得分:3)
您需要中性元素来结束递归。你不是每一步都需要它。我几乎可以说中性元素属于运算符,而不属于reduce。像这样:
operation_neutral(add, 0).
operation_neutral(mult, 1).
然后,您的reduce
可能如下所示:
reduce([], Op, Result) :- operation_neutral(Op, Result).
reduce([X|Xs], Op, Result) :-
reduce(Xs, Op, R0),
call(Op, X, R0, Result).
但是现在让我们保持容易,并按照问题中的建议去做。只有在操作数列表为空时才需要中性元素,以设置结果:
reduce([], _, N, N).
否则,进入递归然后将操作应用于结果(并且可以直接使用call
):
reduce([X|Xs], Op, N, R) :-
reduce(Xs, Op, N, R0),
call(Op, X, R0, R).
您可以see here reduce/4
如何append/3
使用N
。
您在call
内看到add(X, Y, R) :- R is X+Y.
mult(X, Y, R) :- R is X*Y.
了吗?我没有看到它,它不属于那里。
为完整起见,两位运营商:
reduce/3
也没有提及中性元素。
顺便说一下:你所谓的“减少”也被称为列表上的“折叠”。您可以按foldl
这样定义reduce(List, Op, Result) :-
operation_neutral(Op, N),
foldl(Op, List, N, Result).
:
call
递归和foldl
都发生在reduce/4
内,您根本不需要template <typename A, typename Op>
A reduce(A[] operands, Op op) {
return reduce(operands, op, 0);
}
template <typename A, typename Op>
A reduce(A[] operands, Op op, int n) {
if (n == operands.length) return neutral<Op>();
return op(operands[n], reduce(operands, op, n+1));
}
。
您如何用程序语言解决这个问题?使用类似C语法的伪代码,并假设我们对泛型有一些支持,它可能是这样的:
foldl
(显然,如果这确实是C ++,我们将使用迭代器,而不是索引......)
与Prolog版本完全不同。如果你以与上面的template <typename A, typename Op>
A reduce(A[] operands, Op op) {
A x = neutral<Op>();
for (int n = 0; n < operands.length; ++n)
x = op(x, operands[n]);
return x;
}
相同的方式完成它,它可能看起来像这样:
op()
同样,没有提到循环中的中性元素,也没有提到Import-Csv test.csv | Group-Object -Property "Comment" | ForEach-Object {
$path = $_.Name + ".csv";
$_.Group | Export-Csv -Path $path -NoTypeInformation
}
的调用。
答案 1 :(得分:3)
根据上述评论以及您仍然是Prolog初学者的事实,我将尝试解释一些可能不太清楚的事情:
<强>
What are variables in Prolog and what is instantiation ?
强>
在Prolog中,变量是以大写字母和匿名变量_
开头的(只是下划线定义了一个匿名变量,它是一个没有名字的变量)。 Prolog中的变量意味着它们可以表示任何列表,字符串,原子(......无论如何),当你使用新的变量时它没有被实例化,这意味着它可以代表任何东西,但现在它代表它 - 它没有& #39; t 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与数字2统一,或X = [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)).
放在您的文件中,然后使用运算符#=
代替{{ 1}}。现在你可以尝试is/2
,它将使用数字2甚至X #= 2
实例化变量X,现在你没有得到实例化错误,但你得到答案2 #= 1 + X
,所以你可以看到它是现在更关系。
现在问题......
我同意@Boris中立不应该通过递归进行,而是用函数声明。虽然我会尽力跟上你的回答:
首先,当我尝试使用swi prolog中的代码查询文件时,我得到:
X = 1
这表示变量F,Neutral,EE是单独的条款:
Warning: /Users/Desktop/reduce.pl:1:
Singleton variables: [F,NEUTRAL,EE]
正如您所看到的,您不会在本节中的任何地方使用变量F。
在我看来,reduce谓词应该以空列表失败,因为你不能用中性元素和带两个参数的函数做任何操作。因此,作为基本情况,我将使用仅包含一个元素的列表来定义:
reduce([],F,NEUTRAL,EE).
然后如上所述,规则如下:
reduce([H],F,NEUTRAL,EE):-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal).
会导致实例化错误,因为在EE中是var而你不能在is / 2中使用它,但即使它被实例化你也不能使用它,因为解释add(N1,NEUTRAL, EE) :- EE is EE + N1 + NEUTRAL.
将会不起作用,因为 EE is EE + whatever
为了解决这个问题,我会尝试使用另一个变量进行下一次递归,然后计算结果如下:
you try to reinstantiate a variable..!!
(这里添加了一个新变量EE1进行递归调用然后计算 结果基于将返回的EE1的结果 来自递归电话。)
现在让我们测试一下:
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).
通过按&#34;&#34;给你结果?- reduce([1,2,3,4],add,0,L).
L = 10 ;
false.
?- reduce([1,2,3,4],mult,1,L).
L = 24 ;
false.
时还有一件事。我们要求更多的答案并返回false,因为没有可能的答案。如果谓词成功或失败,那么错误或真实的东西只是Prolog的一种方式告诉你。例如:
L=10
这说明上述情况属实,当我们询问是否有其他方法可以为真时,它会返回false ...
这就是它,毕竟Prolog似乎并不那么糟糕:)(根据你的描述)。
作为练习,您可以尝试使用CLPFD编写相同内容,以便更具关系性,并能够回答如下问题:?- reduce([1,2,3,4],mult,1,24).
true ;
false.
以及@ Boris关于不通过递归一直进行中立的想法
<强> 修改 强>
根据@false建议和有用的评论,我将使用call / N写第二种方式,这显然更好(见评论......):
reduce(L,mult,1,24).
答案 2 :(得分:-3)
我希望这个部分答案不会太偏离主题,从而使其无法参与本次讨论。
关于OP声明“对于相同的样本查询,例如? - reduce([],add,0,R)。我们只得到一个布尔值(在这种情况下是真的)”重要的是对一个什么有一个很好的解释你的意思是“真实”。在我看来,将您识别的“返回值”(.i.e。(隐式)返回值)视为具有“布尔”类型是不合适的。这是因为使用布尔类型&gt;&gt;“false”表示相反为true&lt;&lt; 。在特定上下文中的prolog中(.ie关于(隐式)返回值)布尔类型期望的应用不适用,因为在该类型和非类似的情况下,布尔类型---&gt;&gt;“ false“表示不同而不是”true“&lt;&lt;
答案 3 :(得分:-3)
如前面的答案所述,你真的希望消除对call/99
的依赖。
这是让你入门的东西。 它是您原始的完全功能失常的代码,具有不同的语法。 我的意思是,我希望提出一个可能对你有用的合成词,但原文的破碎语言保持不变。
% [ reduce , VECTOR , FUNCTION , NEUTRAL , EE ]
:- [library(clpfd)] .
[reduce , [] , F , NEUTRAL , EE] :- ( true ) .
[reduce , [N|NN] , F , NEUTRAL , EE]
:-
(
[F , N , NEUTRAL , EE] ,
[reduce , NN , Z , NEUTRAL , EE]
)
.
[add , N , NEUTRAL , EE] :- ( EE #= EE + N + NEUTRAL ) .
[mult , N , NEUTRAL , EE] :- ( EE #= N * EE * NEUTRAL ) .
这是便于比较的原件......
%
reduce([],F,NEUTRAL,EE).
reduce([H|T],F,NEUTRAL,EE) :-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal),
reduce(T,F,NEUTRAL,EE).
add(N1,NEUTRAL, EE) :- EE is EE + N1 + NEUTRAL.
mult(N1,NEUTRAL, EE) :- EE is N1 * EE * NEUTRAL.