约束条件下的 Picat 函数

Picat functions in constraints

在介绍性练习中,我的目标是根据各种约束生成 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 的正确方法是什么?

您定义 sum1/1 的问题在于您将决策变量(使用 :: 定义)与“普通”非决策变量(使用 :=)混合在一起。这不起作用,因为您无法重新分配决策变量的值(示例中的变量 N)。

此外,不幸的是,您不能在定义约束时使用函数。它不适用于 return 决策变量的值(即使用 #=)。

这是我的做法。它有点复杂,因为它支持决策变量何时位于列表中以及何时位于数组中(如您的示例所示)。如果它是一个数组(由 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).

进一步评论: 请注意,head 中的保护条件(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) 来访问 array/list 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).

Update:这里有一个更通用的版本,它的形式与上面的 sum2/2-3fold2(X,Predicate,Init,Result) 相似。输入参数是 list/array X,谓词 Pred(元数为 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/2prod/2(对于 list/array 中元素的乘积),再次检查它是数组还是列表。

% 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).