约束中的 Picat 函数

问题描述

在介绍性练习中,我的目标是生成 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)。

另外,不幸的是,定义约束时不能使用函数。返回决策变量的值不起作用(即使用 #=)。

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

% X is a list.
sum2(X,list(X) =>
  sum2(X,Sum).

% sum2/3 with the accumulator
sum2([],Sum1,Sum2) ?=> Sum1 #= Sum2.
sum2([H|T],Sum0,Sum) ?=> 
  Sum1 #= Sum0 + H,sum2(T,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([],Sum2) ?=> Sum1 #= Sum2.

这是一个变体 - 仍然是递归的 - 它适用于列表和数组,无需转换为列表。主要区别在于它使用索引 (Ix) 来访问数组/列表 X 中的值。它有两个累加器:一个用于每一步递减的索引 (Ix),另一个用于中间和 (Sum0)。

sum4(X,Sum) ?=> 
  sum4(X,X.len,Sum).

sum4(X,1,Sum) ?=> Sum #= Sum0 + X[1].
sum4(X,Ix,Sum) ?=>
   Ix > 1,Sum1 #= X[Ix] + Sum0,sum4(X,Ix-1,Sum).

更新:这里有一个更通用的版本,它的形式与上面的 sum2/2-3 类似:fold2(X,Predicate,Init,Result)。输入参数是列表/数组X、谓词Pred(元数为3,示例见下文)和初始值Init;输出参数是结果 Result。我们可以在这里

fold2([],_P,Res0,Res) ?=> Res #= Res0.
fold2([X|T],P,Res) ?=>
  call(P,X,Res1),fold2(T,Res1,Res).  

现在我们可以像这样定义 sum/2prod/2(用于列表/数组中元素的乘积),再次检查它是数组还是列表。

% Multiplication
mult(X,Y,Z) =>
  Z #= X * Y.

% Addition
add(X,Z) =>
  Z #= X + Y.

% Define sum/2 for array
sum5(X,array(X) ?=>
  fold2(X.to_list,add,Sum).

% sum/2 for list
sum5(X,list(X) ?=>
  fold2(X,Sum)

% Define prod/2 for array
prod5(X,Prod),Prod).

% prod2/2 for list
prod5(X,Prod).