【问题标题】:How implement tarai in guile如何在 guile 中实现 tarai
【发布时间】:2019-08-28 02:58:40
【问题描述】:

我现在想运行 tarai,它在 Prolog 中的内容如下。一个测试用例将运行?- tarai(12,6,0,X)。这是一个相当困难的测试用例,例如 GNU Prolog 在这个测试用例中崩溃。

tarai(X, Y, Z, R) :- 
    X > Y -> 
        X1 is max(0,X-1), tarai(X1, Y, Z, Rx),
        Y1 is max(0,Y-1), tarai(Y1, Z, X, Ry),
        Z1 is max(0,Z-1), tarai(Z1, X, Y, Rz),
        tarai(Rx, Ry, Rz, R); 
    R = Y.

我最感兴趣的是测试用例是否可以在 tarai 的一些 miniKanren 代码的完全声明性版本上运行。或者,我有兴趣向后运行一些测试用例。

我有点不知所措。我设法安装了方案变体 guile,并且可以成功运行 miniKanren 测试用例。但是miniKanren没有整数,怎么办呢?

【问题讨论】:

    标签: guile


    【解决方案1】:

    请注意,如果该算法存在并且是确定性的,则它只有一个解决方案。您可以将方案变量作为 x,y,z 直接传递给 kanren tarai 函数,没有统一的那些因此实现 X,Y,Z 的逻辑可以在没有 kanren 变量的情况下完成。但是,R 值需要是逻辑变量,并且您需要以 tarai(Rx,Ry,Rz,R) 形式获取有界值,例如查找 Rx,Ry,Rz 的值并将其输入到 tarai 函数中。此外,您需要确保在前三个表单完成后执行此表单(这很容易做到,因为没有纯粹的多项选择),以便您知道 Rx,Ry,Rz 是有界的。另请注意,该算法可能取决于执行顺序以便有效执行,但确定性再次意味着这一点很容易满足。注意 A -> B ; C 在这里简单地转换为方案(如果 A B C),因为 A = X > Y 是确定性的。所以代码在伪代码中可能看起来像这样

    (define (tarai x y z r)
      (lambda ()
        (fresh (rx ry rz)
           (if (> x y)
               (conda
                 ( (conda ((tarai (- x 1) y z rx) 
                           (tarai (- y 1) z x ry)
                           (tarai (- z 1) x y rz)))
                   (project (rx ry rz) (tarai rx ry rz r))))
               (== r y)))))
    

    【讨论】:

    • 这个周末我在打电话,所以只是简短的回答。不,序言代码也不可逆。但是如果你想要可逆性,你需要用小于、大于或等于来保护每个子句。在此之后,您需要一个 wsy 来约束变量并使用变量进行算术运算。在序言中,您可以使用诸如 clpfd 之类的常量求解器或它所调用的东西来执行此操作,也许 numbers.scm 会提供相同的功能。
    • 如果你没有最大值和最小值,你可以使用警卫 x > 0 和 x=0 来潜水
    • 如果你没有conda,试试上面例子中的conde
    【解决方案2】:

    要在 guile-log 方案宏接口see links on this site 中进行确定性验证,您可以使用 memoization 来实现它(没有 memoization,解决方案会破坏 guile-log 上的变量堆栈)

    (define tarai 
      (memo 
        (<lambda> (x y z r) 
           (if (> x y) 
               (<var> (rx ry rz) 
                  (<and> 
                     (tarai (max (- x 1) 0) y z rx) 
                     (tarai (max (- y 1) 0) z x ry) 
                     (tarai (max (- z 1) 0) x y rz) 
                     (tarai (<lookup> rx) (<lookup> ry) (<lookup> rz) r))) 
               (<=> r y)))))
    
    scheme@(guile-user)> ,time (<run> 1 (r) (tarai 12 6 0 r))
    $13 = (12)
    ;; 0.293411s real time, 0.290711s run time.  0.000000s spent in GC.
    scheme@(guile-user)> 
    

    【讨论】:

    【解决方案3】:

    我尝试使用 clpfd 运行例如 tarai(12,X,0,12),但它太复杂了。并且记忆不适用于 swipl 的属性变量。因此,我能找到的最佳解决方案 atm 是将确定性记忆 tarai 与类似的东西一起使用

    tarai2(X,Y,Z,W) :-
      (var(X)->between(0,20,X);true),
      (var(Y)->between(0,20,Y);true),
      (var(z)->between(0,20,Z);true),
      tarai(X,Y,Z,W).
    

    然后可以很容易地找到该范围内的所有解 Y,Z 与 tarai2(12,Y,Z,12)。

    【讨论】:

    • 稍后让我删除帖子,我们可以清理。
    【解决方案4】:

    这个问题被重新提出来询问如何在 prolog 规范中实现更通用版本的 tarai 函数,该版本允许 x、y、z 字段中的变量。这里的技术可以在 prolog 中实现,例如kanren 需要 clpfd 有限域求解器和类似的东西(参见上面的评论讨论,例如对 numbers.scm 的引用)。关键是 nuke -> 并在所有情况下使用警卫,我们将假设运算符 >o =o 0 会将 X 限制为值 1,2 ,3,...)。此外,我们将假设通过特殊的“iso”使用区间算术约束为此类变量定义了“-o”。使用它,我们可以将 tarai 定义为下面的代码(如果 max 和 min 也被定义为约束,这可以简化,但这里我们通过不等式和一些案例来实现这些)。

    (define (taray x y z w)
      (lambda ()         
        (conde ((<o x y) 
                (fresh (rx ry rz)
                  (conde
                   ((conde 
                      ((>o x 0) 
                       (fresh (xx) 
                         (conde
                           ((iso xx (-o x 1)) 
                            (tarai xx y z rx)))))
                      ((== x 0) (tarai 0 y z rx)))
                    (conde 
                      ((>o y 0) 
                       (fresh (yy) 
                         (conde
                           ((iso yy (-o y 1)) 
                            (tarai yy z x ry)))))
                      ((== y 0) (tarai 0 z x ry)))
                    (conde 
                      ((>o z 0) 
                       (fresh (zz) 
                         (conde
                           ((iso zz (-o z 1)) 
                            (tarai zz x y rz)))))
                      ((== z 0) (tarai 0 x y r<)))
                    (tarai rx ry rz r)))))
                ((>=o x y) (== x y))))))
    

    【讨论】:

    • 我可以加一个 o,如果你想真正标记那些是 'o 类型的
    • 我没有指向 number.scm atm 的链接,因为它被防火墙阻止了。我使用一个假设的约束库,例如一个好的应该支持像clpfd这样的人
    • 另请注意,能够像在 clpfd 中一样使用 iso 构造引入约束是明智的。
    • 要允许 x 或 y 或 z 或 w 成为变量,您需要一个 minikanren 的有限域求解器,您需要自己编写代码,因为我在网上找不到这样的库,如果 x, y,z, 是有界的,你可以使用我的第一个版本。我希望你的追求是在 numbers.scm 中定义了不等式,但它看起来就像你描述的好书“合理的计划者”中完成的常用数字的库。顺便说一句,我回馈不是为了赏金,而是为了回馈社区,SE 是一个很棒的资源。
    • 以前这个是不正确的,如果能用max就简单多了
    【解决方案5】:

    我是guile-log 的开发人员,这是一个基于 guile 方案的逻辑编程环境,它同时具有 minikanren 构造和 prolog 构造,并且可以混合使用它们。它还移植了一个 clpfd 库,因此您可以在这里执行以下操作(不幸的是 atm 它不起作用(我正在处理的一个错误))。假设导入了 clpfd。 (,, ;; 像操作一样交错看人)。将 ,, 替换为 , 和 ;;和 ;您可以使用 clpfd 库获得可以在例如 swi prolog 上运行的代码。

    tarai(X,Y,Z,W) :-
     (
       X #> Y , 
       (
         (
           ((X #> 0 , XX #= X - 1, tarai(XX,Y,Z,RX)) ;;
            (X  = 0 , tarai(0,Y,Z,RX))) ,,
           ((Y #> 0 , YY #= Y - 1, tarai(YY,Z,X,RY)) ;;
            (Y =  0 , tarai(0,Z,X,RY))) ,,
           ((Z #> 0 , ZZ #= Z - 1, tarai(ZZ,X,Y,RZ)) ;;
            (Z =  0 , tarai(0,X,Y,RZ)))
         ) ,,
         tarai(RX,TY,RZ,R)
       )
     ) ;;
     (X #=< Y, R=Y).
    

    【讨论】:

    • 我个人认为突出不同的想法和语法很好
    • 请注意,您可以将 ,, 更改为 , 和 ;;到 ;并在 swi prolog 上运行代码,但是你没有得到 minikanren 对无限递归的保证。
    • 深度优先搜索在某些情况下可能会导致无穷大,而 minikanren 的好处是您可以编写代码并知道如果有解决方案,除非内存不足,否则您最终会找到它。但是在选择搜索策略时并没有太多的智能,明智的做法是使用其他方法来明智地指导搜索。这可能是您在运行 tarai 时看到的。要寻找的一件事是可以加快速度的记忆。
    • 可能慢了一个数量级。一个人必须运行它才能确定。 Guile 的编译器虽然很慢。 15 分钟编译 clpfd。
    • 我在 guile-log 方案宏接口中添加了一个确定性的计时答案
    【解决方案6】:

    这是一个缓慢的实现,它是声明性的,并使用numbers.scm(如问题作者所建议的那样)和 minkanren。

    (load "minkanren.scm)
    (load "numbers.scm")
    
    (define one  (build-num 1))
    (define zero (build-num 0))
    (define (tarai x y z r)
        (conde
            ((<=o x y) (== r y))
            ((<o y x)
             (fresh (rx ry rz)
              (conde
               ((fresh (xx)
                (conde
                 ((<o zero x) (minuso x one xx) (tarai xx y z rx))
                 ((== zero x) (tarai zero y z rx))))
               (fresh (yy)
                (conde
                 ((<o zero y) (minuso y one yy) (tarai yy z x ry))
                 ((== zero y) (tarai zero z x ry))))
               (fresh (zz)
                (conde
                 ((<o zero z) (minuso z one zz) (tarai zz x y rz))
                 ((== zero z) (tarai zero x y rz))))
               (tarai rx ry rz r)))))))
    

    如果推导方向相反(所以我们添加数字)由于声明式风格我们仍然可以解决,

    tarai(4,2,Z,4):, (you need to make binaries of the number) will lead to
    Y = [0, 1],
    W = [0, 0, 1],
    Z = [0, 0, 1],
    X = [0, 0, 1].
    

    假设如果添加表格:

    tarai(12,6,0,W):
    Z = (),
    X = [0, 0, 1, 1],
    Y = [0, 1, 1],
    W = [0, 0, 1, 1].
    

    【讨论】:

    • 看起来当您反转推导时,它开始计数元素并且案例数量爆炸并且搜索算法接缝永远需要。限制数字会阻碍这一点,它与搜索之间有点不同,因为搜索顺序不同。
    • 是的,它使用了一些 guile-log 宏来本质上启用表格并使其可在 prolog 提示符下运行。
    • aha
    • 告诉我你想留下哪一个,我们可以让它成为那样!
    • 您的示例是 tarai(12,6,0,R),如果您可以要求 tarai(12,Y,0,12),则可以反转,例如您可以解决任意数量的变量,这会导致多种解决方案。如果你不想要表格,你可以很容易地在上面的帖子中有一个 minikanren 解决方案,这是声明性的,尽管它会在例如之间使用交错。
    猜你喜欢
    • 2020-09-10
    • 2023-03-31
    • 2016-11-29
    • 2023-03-22
    • 2016-08-14
    • 2021-11-17
    • 2018-05-28
    • 2019-07-01
    • 2013-09-02
    相关资源
    最近更新 更多