在列表中的Prolog中计算算术

时间:2017-05-24 17:00:59

标签: prolog arithmetic-expressions meta-predicate

我们目前正致力于谓词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的初学者而太复杂;)

4 个答案:

答案 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.