List Prolog-查找列表的乘积

List Prolog-查找列表的乘积,list,prolog,List,Prolog,我是prolog(swi prolog)的新手,如果这是一个愚蠢的问题,那么很抱歉。这也是一项大学作业。我被要求查找列表的产品,这是我想到的: product([],1). product([H|T], Product) :- product( T, Product1), Product is H * Product1. 最长时间内,我的基本情况如下: product([],0). 但这一切都是零。但是,在测试 产品([],产品),我得到一个-这是不正确的。任何关于解决方案的

我是prolog(swi prolog)的新手,如果这是一个愚蠢的问题,那么很抱歉。这也是一项大学作业。我被要求查找列表的产品,这是我想到的:

product([],1).
product([H|T], Product) :-
    product( T, Product1),
    Product is H * Product1.
最长时间内,我的基本情况如下:

product([],0).
但这一切都是零。但是,在测试
产品([],产品)
,我得到一个-这是不正确的。任何关于解决方案的提示都将不胜感激。

这是正确的:1是乘法下“数字”的形式。例如,您可以抽象出操作和循环,以获得以下内容:

list_op_product(L, Op, P) :-
    identity_element(Op, IE),
    reverse(L, R), % no foldr in SWI-Prolog, at least
    foldl(Op, R, IE, P).
因此,您只需要定义
identity\u元素/2
和操作本身。所以,加法和乘法应该是这样的:

identity_element(add, 0).
identity_element(mult, 1).

add(X, Y, R) :- R is X + Y.
mult(X, Y, R) :- R is X * Y.
然后你可以说:

?- list_op_product([2,3,4], add, Sum).
Sum = 9.

?- list_op_product([2,3,4], mult, Product).
Product = 24.
类似地,字符串连接的标识元素是空字符串。因此,如果您只是将以下子句添加到
identity\u元素/2

identity_element(atom_concat, '').
你现在可以说:

?- list_op_product([a,b,c], atom_concat, R).
R = abc.
当然,这大部分都是不必要的,但这说明了问题的关键

至于:也许有一种比切割更干净的方法来避免错误的答案:

product([], 0).
product([H|T], P) :-
    product_1(T, H, P).

product_1([], P, P).
product_1([H|T], H0, P) :-
    product_1(T, H, P0),
    P is P0 * H0.
所以现在:

?- product([2,3,5], P).
P = 30.

?- product([], P).
P = 0.
但这感觉不对。您仍然应该有一个基本案例:

product([], 1).

product/2
的这一条款仅定义了您的身份元素;这就是为什么最好将1保留在那里,而不是将其替换为0!但是,对于非空列表,您将少做一次乘法(您将不会有最后一次
*1
)。这是一个有效且易于进行优化的方法。如果您试图查找一个类型完全没有标识元素的产品:那么,
product([],X)
未定义。您可以省略该子句,然后
?-product([],X)
将失败。或者,您可能会抛出错误?

我同意@Boris表达的观点,但我认为如果您希望空列表的结果为0,则可以利用模式匹配,这是Prlog的一个显著特征:

product([], 0).
product([N], N).
product([H|T], Product) :-
    product(T, Product1),
    Product is H * Product1.
由于引入的基本情况与递归子句重叠,因此需要进行剪切以避免

?- product([2,3,5],X).
X = 30 ;
X = 0.
因此,请根据需要更改我的建议

编辑以避免剪切,可以使用替代图案

product([], 0).
product([N], N).
product([H,I|T], Product) :-
        product([I|T], Product1),
        Product is H * Product1.

现在第二和第三个子句不再重叠,但可能是您的Prolog不够聪明,无法优化谓词…

我建议空列表的乘积应该是1

第一个原因,正如Capelical的答案所示,如果你要求它为0,那么你就有一个逻辑上的不连续性。谓词需要特殊处理来处理0产品与1产品

空列表的乘积为0没有特殊原因。事实是:

foo([], X).
描述
[]
X
之间的关系
foo
X
应该是什么?列表为空并不意味着逻辑答案为
0

让我们从另一个角度来看。假设我有两个数字列表,
L1
L2
。假设这些列表的乘积分别为
p1
p2
。如果我有:

append(L1, L2, L), product(L, X).
X
应该是什么?我想应该是p1 x p2。如果
L1=[2,3]
L2=[4,5]
,那么我们有
p1=6
p2=20
<代码>L=[2,3,4,5],其乘积为120,即
6 x 20
。到目前为止还不错

现在假设
L2=[]
。然后
L=[2,3]
。如果
产品(L2,0)
,那么我将拥有
p2=0
,和
p1xp2=0
。但是乘积(L,6)。所以逻辑崩溃了,我们有数学或逻辑上的不连续性。但是,如果
产品([],1)。
,则所有这些都能按预期工作


在算术意义上,代数恒等式是默认情况下如果没有的话所拥有的。对于求和,标识为0,我可以将
a
写成
a+0+…+0
并且仍然有
a
。同样地,
a
可以写成
ax1x。。。x 1
。因此,
乘积([],1)在数学上是有意义的。
为真。

通常在描述涉及列表的关系时,从描述两种情况开始是有意义的:一种用于空列表,另一种用于至少涉及一个元素的列表

在第二个子句中,可以使用
foldl/4
和一个小的辅助谓词。例如:

list_product([], 1).
list_product([L|Ls], P) :-
        foldl(product_, Ls, L, P).

product_(A, B, P) :- P is A*B.
事实上,你可以这样写:

list_product(Ls, P) :- foldl(product_, Ls, 1, P).
空列表的乘积被合理地定义为1,这样,当在多个列表上构建产品时,空列表自然地混合在一起,并且不会改变任何结果

示例:

?- list_product([1,2,3,4], P).
P = 24.
以及:

?地图列表(列表产品,[[1,2],[3,4]],Ps),列表产品(Ps,P)。 P=24, Ps=[2,1,12]。
我不认为这是错误的。乘法下实数或整数的单位元素实际上是1。我同意Boris的观点:
乘积([],1)
应该是正确的基本情况,因为1是乘法单位。答案很好,但请参阅我的答案中的加法,了解避免切分的直接方法(当然你已经知道了).虽然这种效率在Prolog这样的语言中很少出现问题,但我猜。我之所以展示这段代码,是因为它没有切口,也没有太大的杀伤力(就像折叠一样)。我认为需要切口来避免
X
的错误答案这一事实强调了一个事实,即基本情况应该是
积([],1)
,因为1是乘法标识。在数学中,在某种意义上,当一无所有时,标识元素是默认的。所以你有一些事实,比如
a=ax1x1x。。。x 1
用于任何数量的1,因为我可以在不更改值的情况下使用标识继续操作。换句话说,当不存在任何内容时,将假定标识(1)。也许
产品([],X)
可能会失败,但是
X=0
是错误的答案,我认为这不是问题 ?- maplist(list_product, [[1,2],[],[3,4]], Ps), list_product(Ps, P). P = 24, Ps = [2, 1, 12].