我是prolog(swi prolog)的新手,很抱歉,如果这是一个愚蠢的问题。这也是一项单一的任务。我被要求找到列表的产品,这就是我想出的:
product([],1).
product([H|T], Product) :-
product( T, Product1),
Product is H * Product1.
最长的我有基本情况:
product([],0).
但这使一切都归零。但是,在测试基本情况时
product([],Product)
,我得到一个 - 这是不正确的。任何解决方案的提示都将受到赞赏。
答案 0 :(得分:6)
这不是不正确的:1是乘法下“数字”的identity element。例如,您可以抽象出操作和循环,以获得以下内容:
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_element/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_element/2
:
identity_element(atom_concat, '').
你现在可以说:
?- list_op_product([a,b,c], atom_concat, R).
R = abc.
当然,大部分内容都是不必要的,但它说明了这一点。
至于other answer:为了避免错误答案,可能会采用比剪辑更简洁的方法:
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)
将失败。或者,您可能会抛出错误?
答案 1 :(得分:3)
我建议空列表的产品应为1。
第一个原因,正如CapelliC的答案所示,如果你要求它为0,那么你就有一个逻辑上的不连续性。谓词需要特殊处理来处理0产品而不是1。
空列表的产品没有特殊原因为0.事实:
foo([], X).
描述foo
和[]
之间的关系X
。 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]
。如果product(L2, 0)
,那么我会p2 = 0
和p1 x p2 = 0
。但product(L, 6).
所以逻辑分解,我们有数学或逻辑上的不连续性。但是,如果product([], 1).
,那么这一切都按预期工作。
在算术意义上,如果没有任何东西,代数身份就是默认拥有的。对于总和,标识为0,我可以将a
写为a + 0 + ... + 0
,但仍然有a
。同样,a
可以写成a x 1 x ... x 1
。因此,product([], 1).
的数学意义是正确的。
答案 2 :(得分:3)
通常在描述涉及列表的关系时,从描述两种情况开始是有意义的:一种用于空列表,一种用于涉及至少一个元素的列表。
在第二个子句中,您可以使用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).
事实上,你可以将其写成:
?- list_product([1,2,3,4], P).
P = 24.
空列表的产品明智地定义为1,因此,当在多个列表上构建产品时,空列表会自然地混合并且不会更改任何结果。
示例:
{!! file_get_contents(asset('images/icon.svg')) !!}
和
?- maplist(list_product, [[1,2],[],[3,4]], Ps), list_product(Ps, P). P = 24, Ps = [2, 1, 12].
答案 3 :(得分:2)
我同意@Boris表达的观点,但我认为如果你想要一个空列表的结果为0,你可以利用模式匹配,这是Prlog的一个显着特点: / p>
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不够聪明,无法优化谓词......