【问题标题】:Picat functions in constraints约束中的 Picat 函数
【发布时间】:2021-04-18 17:00:46
【问题描述】:

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

【问题讨论】:

    标签: picat


    【解决方案1】:

    您对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/2prod/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).
    

    【讨论】:

    • 使用我的约束的修改版本并使用您的 sum2/3 一切正常。我的猜测是内置的 sum/1 与添加的语法糖相似。新的约束是sum2([Codes[I] : I in 1..CodeLen], 0, Sum), Sum #=3。感谢您非常有帮助的回答。
    • 别的,如果可以的话。您能否简要解释一下@<, @=<, @<=, @>, @>= 运算符的作用? Picat Guide 的定义并没有帮助我掌握它们的功能。
    • 它对你有用。 @< 等是适用于任何术语的比较运算符,例如原子、列表、字符串、结构(不仅仅是数字如< 等)。尽管它们不适用于决策变量(那么您应该使用#< 等)。希望这能回答你的问题。
    • 你能看看我下一个关于生成格雷码的问题吗?谢谢。
    猜你喜欢
    • 1970-01-01
    • 2013-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多