您对sum1/1 的定义的问题在于,您将决策变量(使用:: 定义)与“普通”非决策变量(使用:=)混合在一起。这不起作用,因为您无法重新分配决策变量的值(在您的示例中为变量 N)。
另外,不幸的是,在定义约束时不能使用函数。返回决策变量的值不起作用(即使用#=)。
我会这样做。它有点复杂,因为它支持决策变量何时在列表中以及它们何时在数组中(如您的示例中所示)。如果它是一个数组(由array(X) 检查),那么我们必须将它转换为一个列表,因为[H|T] 的东西只适用于列表,而不是数组。该定义本身是使用累加器的逻辑编程求和的“标准”定义。
% If X is an array, convert it to a list and then call sum2/3
sum2(X,Sum), array(X) =>
sum2(X.to_list,0,Sum).
% X is a list.
sum2(X,Sum), list(X) =>
sum2(X,0,Sum).
% sum2/3 with the accumulator
sum2([],Sum1,Sum2) ?=> Sum1 #= Sum2.
sum2([H|T],Sum0,Sum) ?=>
Sum1 #= Sum0 + H,
sum2(T,Sum1,Sum).
进一步评论:
请注意,头部中的保护条件(array(X) 和 list(X))仅适用于定义谓词的 Picat 样式(?=> 和 =>),而不适用于 Horn 子句样式(:-,在 v3 中引入.0),这意味着不能依赖于谓词头部的匹配。因此,这在使用 Picat 样式时不起作用:
sum2([],Sum,Sum) ?=> true. % Matching in head don't work using Picat style.
两个Sum 变量必须在头部不同,然后在主体中相等(使用#=),即
sum2([],Sum1,Sum2) ?=> Sum1 #= Sum2.
这里有一个变体 - 仍然是递归的 - 它适用于列表和数组,而无需转换为列表。主要区别在于它使用索引(Ix)来访问数组/列表X 中的值。它有两个累加器:一个用于索引(Ix),每一步递减,一个用于中间和(Sum0)。
sum4(X,Sum) ?=>
sum4(X,X.len,0,Sum).
sum4(X,1,Sum0,Sum) ?=> Sum #= Sum0 + X[1].
sum4(X,Ix,Sum0,Sum) ?=>
Ix > 1,
Sum1 #= X[Ix] + Sum0,
sum4(X,Ix-1,Sum1,Sum).
更新:这里有一个更通用的版本,它的形式与上面的sum2/2-3 相似:fold2(X,Predicate,Init,Result)。输入参数是列表/数组X、谓词Pred(arity 3,示例见下文)和初始值Init;输出参数是结果Result。在这里'我们可以
fold2([],_P,Res0,Res) ?=> Res #= Res0.
fold2([X|T],P,Res0,Res) ?=>
call(P,Res0,X,Res1),
fold2(T,P,Res1,Res).
现在我们可以像这样定义sum/2 和prod/2(用于列表/数组中元素的乘积),再次检查它是数组还是列表。
% Multiplication
mult(X,Y,Z) =>
Z #= X * Y.
% Addition
add(X,Y,Z) =>
Z #= X + Y.
% Define sum/2 for array
sum5(X,Sum), array(X) ?=>
fold2(X.to_list,add,0,Sum).
% sum/2 for list
sum5(X,Sum), list(X) ?=>
fold2(X,add,0,Sum)
% Define prod/2 for array
prod5(X,Prod), array(X) ?=>
fold2(X.to_list,add,1,Prod).
% prod2/2 for list
prod5(X,Prod), list(X) ?=>
fold2(X,add,1,Prod).