2

在介绍性练习中,我的目标是生成 0、1 值的模式,并受到各种约束。虽然下面的代码适用于内置 sum/1 函数,但对于手工制作的 sum1/1 版本,它会失败(invalid_constraint_expression)。

import cp.
main => fill_0_1(4).

fill_0_1(CodeLen) =>
    Codes = new_array(CodeLen),
    Codes :: 0 .. 1,

    sum1([Codes[I] : I in 1..CodeLen]) #= 3,
    solve(Codes),
    printf("%w\n", Codes).

sum1(FDVars) = N =>
    N := 0,
    foreach (V in FDVars)
        N := N + V
    end.

创建 sum1 的正确方法是什么?

4

1 回答 1

2

您定义的问题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).
于 2021-04-18T18:54:53.933 回答