您定义的问题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)
and list(X)
) 中的保护条件仅适用于定义谓词 ( ?=>
and =>
) 的 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)
. 输入参数是 list/array 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).