list 如何获得序言中给定数字的总和?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11520621/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I get the sum of given numbers in prolog?
提问by Zik
I'm new to prolog and I'm doing some exercises for practice. So I'm trying to get the sum of the given numbers in a list. I'm trying to use this:
我是 prolog 的新手,我正在做一些练习练习。所以我试图获得列表中给定数字的总和。我正在尝试使用这个:
my_last(X, [X]).
my_last(X, [_|L]) :- my_last(X, L).
(from here)
(从这里)
as my guide. So this is my code for getting the sum:
作为我的向导。所以这是我获取总和的代码:
listsum(X, []).
listsum(X, [H|L]):-
X is H + listsum(X, L).
when I compile it, it says
当我编译它时,它说
practice.pl:3: evaluable
listsum(_G139,_G140)
does not exist
practice.pl:2: Singleton variables:[X]
practice.pl:3:evaluable
listsum(_G139,_G140)
不存在
practice.pl:2:单例变量:[X]
then when I try listsum(0, [1,2,3]).
it returns false
.
然后当我尝试listsum(0, [1,2,3]).
它返回false
。
I still don't understand much about prolog, and list and recursion in prolog.
我仍然不太了解prolog,以及prolog中的列表和递归。
回答by m09
Arithmetic
算术
As you already discovered, arithmetic can be handled in Prolog with the (is)/2
operator. It's because in Prolog, everything is only symbolic calculus: things don't have a meaning by default, so the unification (=)/2
wouldn't know that (+)/2
refers to the addition for example.
正如您已经发现的,算术可以在 Prolog 中使用(is)/2
运算符进行处理。这是因为在 Prolog 中,一切都只是符号演算:事物在默认情况下没有意义,因此统一(=)/2
不知道(+)/2
指的是加法。
Now, your problem is that you use a regular predicate inside of (is)/2
(here it's your recursive call). Since (is)/2
only performs arithmetic, it doens't evaluate the predicate call. It doesn't even recognize it since it's not an arithmetic function.
现在,您的问题是您在内部使用了常规谓词(is)/2
(这是您的递归调用)。由于(is)/2
仅执行算术运算,因此不会评估谓词调用。它甚至无法识别它,因为它不是算术函数。
The fix here would be to affect the result of the recursive call to a variable and then use it in the (is)/2
call:
这里的解决方法是影响对变量的递归调用的结果,然后在(is)/2
调用中使用它:
listsum(X,[]).
listsum(Result, [Head|Tail]) :-
listsum(SumOfTail, Tail),
Result is Head + SumOfTail.
Base case correctness
基本情况正确性
But if you test that code you will not get the desired result. The reason is that you have another problem, in your base case. The sum of the empty list isn't "anything", as you stated by writing
但是,如果您测试该代码,您将无法获得所需的结果。原因是你有另一个问题,在你的基本情况下。正如你写的那样,空列表的总和不是“任何东西”
listsum(X,[]).
(X
is a free variable, hence can be anything).
(X
是一个自由变量,因此可以是任何东西)。
Instead, it's 0
:
相反,它是0
:
listsum(0, []).
The resulting code is:
结果代码是:
listsum(0, []).
listsum(Result, [Head|Tail]) :-
listsum(SumOfTail, Tail),
Result is Head + SumOfTail.
Order of arguments
参数顺序
Now, as a sidenote, in Prolog a convention is that output variables should be put at the end of the predicate while input variables should be put at the start of the predicate, so to behave as wanted we could refactor as follows:
现在,作为旁注,在 Prolog 中,一个约定是输出变量应该放在谓词的末尾,而输入变量应该放在谓词的开头,所以为了表现得像我们想要的那样,我们可以重构如下:
listsum([], 0).
listsum([Head|Tail], Result) :-
listsum(Tail, SumOfTail),
Result is Head + SumOfTail.
Tail Call Optimization
尾调用优化
Now, we can still improve this predicate with more advanced techniques. For example we could introduce tail calls so that Tail Call Optimization (googlable) could be performed, thanks to an idiom of declarative programming called an accumulator:
现在,我们仍然可以使用更先进的技术改进这个谓词。例如,我们可以引入尾调用,以便可以执行尾调用优化(googlable),这要归功于称为累加器的声明式编程习语:
listsum(List, Sum) :-
listsum(List, 0, Sum).
listsum([], Accumulator, Accumulator).
listsum([Head|Tail], Accumulator, Result) :-
NewAccumulator is Accumulator + Head,
listsum(Tail, NewAccumulator, Result).
The idea behind that is to update an intermediate result at each step of the recursion (by adding the value of the current head of the list to it) and then just state that when the list is empty this intermediate value is the final value.
其背后的想法是在递归的每一步更新一个中间结果(通过将列表当前头部的值添加到它)然后声明当列表为空时这个中间值是最终值。
Getting more general programs
获取更多通用程序
As you may have noted in Prolog, quite often predicates can be used in several ways. For example, length/2
can be used to discover the length of a list:
正如您在 Prolog 中可能已经注意到的那样,谓词通常可以以多种方式使用。例如,length/2
可用于发现列表的长度:
?- length([1, 2, 3], Length).
Length = 3.
or to build a skeleton list with free variables of a desired length:
或者用所需长度的自由变量构建一个骨架列表:
?- length(List, 3).
List = [_G519, _G522, _G525].
Here though, you might have noted that you can't ask Prolog what are the lists which have a sum that is 6
:
但是,在这里,您可能已经注意到,您不能询问 Prolog 哪些列表的总和为6
:
?- listsum(L, 6).
ERROR: is/2: Arguments are not sufficiently instantiated
That's because, to "go backwards", Prolog would have to solve an equation when comes the call to the (is)/2
operator. And while yours is simple (only additions), arithmetic isn't solvable this way in the general case.
这是因为,要“倒退”,Prolog 必须在调用(is)/2
运算符时求解方程。虽然你的很简单(只有加法),但在一般情况下,算术无法以这种方式解决。
To overcome that problem, constraint programming can be used. A very nice library is available for SWI, clpfd.
为了克服这个问题,可以使用约束规划。一个非常好的库可用于 SWI,clpfd。
The syntax here would be:
这里的语法是:
:- use_module(library(clpfd)).
listsum(List, Sum) :-
listsum(List, 0, Sum).
listsum([], Accumulator, Accumulator).
listsum([Head|Tail], Accumulator, Result) :-
NewAccumulator #= Accumulator + Head,
listsum(Tail, NewAccumulator, Result).
Now we can use our predicate in this other way we wished we could use it:
现在我们可以以另一种方式使用我们的谓词,我们希望我们可以使用它:
?- listsum(L, 6).
L = [6] ;
L = [_G1598, _G1601],
_G1598+_G1601#=6 ;
L = [_G1712, _G1715, _G1718],
_G1712+_G1715#=_G1728,
_G1728+_G1718#=6 . % Here I interrupted the answer but it would not terminate.
We could even ask for all the solutions to the problem:
我们甚至可以询问问题的所有解决方案:
?- listsum(L, X).
L = [],
X = 0 ;
L = [X],
X in inf..sup ;
L = [_G2649, _G2652],
_G2649+_G2652#=X . % Here I interrupted the answer but it would not terminate
I just mentionned that so that you realize that quite often the use of (is)/2
should be avoided and use of constraint programming should be preferred to get the most general programs.
我刚刚提到了这一点,以便您意识到(is)/2
应该经常避免使用 ,并且应该首选使用约束编程来获得最通用的程序。