康达, 康迪, 康德, 康杜

Posted

技术标签:

【中文标题】康达, 康迪, 康德, 康杜【英文标题】:conda, condi, conde, condu 【发布时间】:2012-06-06 07:09:39 【问题描述】:

我正在阅读the Reasoned Schemer。

我对@9​​87654323@ 的工作原理有一些直觉。

但是,我找不到关于 conde/conda/condu/condi 做什么的正式定义。

我知道https://www.cs.indiana.edu/~webyrd/,但这似乎有例子而不是定义。

在某处有condecondacondicondu 的正式定义吗?

【问题讨论】:

【参考方案1】:

用 Prolog 的话来说,

condA“软剪辑” 又名 *->,其中 A *-> B ; C 类似于 (A, B ; not(A), C),只是 更好 ;而

condU“承诺的选择”,是once 和软切的组合,因此(once(A) *-> B ; false) 表示(A, !, B)(带有里面):

condA:     A        *->  B  ;  C        %  soft cut,
                                        %     (A ,    B ; not(A) , C)
condU:     once(A)  *->  B  ;  C        %  committed choice,
                                        %     (A , !, B ; not(A) , C)

(其中;表示“或”,表示“和”,即disjunctionconjunction 目标)。

condA中,如果目标A成功,则所有解决方案都会传递到第一个子句B,并且不会尝试替代子句C

condU 中,once/1 只允许其参数目标成功一次(如果有的话,只保留一个解决方案)。

condE 是连词的简单析取,condI 是其成分的解之间交替的析取, 交错其中的流。


这是一个尝试将本书的代码忠实地翻译成 18 行 Haskell 的代码,不包括逻辑变量和统一,这主要是一个懒惰的 Lisp 语法(*) 看看这是否说明了问题:

顺序流组合(书名“mplus”):
    (1)   []     ++: ys  =  ys
    (2)   (x:xs) ++: ys  =  x : (xs ++: ys)

交替流组合(“mplusI”):

  (3)   []     ++/ ys  =  ys
  (4)   (x:xs) ++/ ys  =  x : (ys ++/ xs)

连续供稿(“bind”):

  (5)   []     >>: g  =  []
  (6)   (x:xs) >>: g  =  g x ++: (xs >>: g)

交替供稿(“bindI”):

  (7)   []     >>/ g  =  []
  (8)   (x:xs) >>/ g  =  g x ++/ (xs >>/ g)

OR目标组合(“condE”):

  (9)   (f ||: g) x  =  f x ++: g x

“交替OR”目标组合(“condI”):

  (10)  (f ||/ g) x  =  f x ++/ g x

AND”目标组合(“all”):

  (11)  (f &&: g) x  =  f x >>: g

“交替AND”目标组合(“allI”书):

  (12)  (f &&/ g) x  =  f x >>/ g

特殊目标

  (13)  true  x  =  [x]  -- a sigleton list with the same solution repackaged
  (14)  false x  =  []   -- an empty list, meaning the solution is rejected

目标在给定问题的(可能部分)解决方案的情况下产生(可能是更新的)解决方案的流(可能是空的)。

all的重写规则是:

(all)      =  true
(all  g1)  =  g1
(all  g1 g2 g3 ...)  =  (\x -> g1 x >>: (all g2 g3 ...))
                     =  g1 &&: (g2 &&: (g3 &&: ... ))
(allI g1 g2 g3 ...)  =  (\x -> g1 x >>/ (allI g2 g3 ...))
                     =  g1 &&/ (g2 &&/ (g3 &&/ ... ))

condX的重写规则是:

(condX)  =  false
(condX (else g1 g2 ...))  =  (all g1 g2 ...)  =  g1 &&: (g2 &&: (...))
(condX (g1 g2 ...))       =  (all g1 g2 ...)  =  g1 &&: (g2 &&: (...))
(condX (g1 g2 ...) (h1 h2 ...) ...)  =  (ifX g1 (all g2 ...) 
                                          (ifX h1 (all h2 ...) (...) ))

要得到最终的condEcondI的翻译,无需实现本书的ifEifI,因为它们进一步简化为简单的运算符组合,所有运算符都被认为是右结合

(condE (g1 g2 ...) (h1 h2 ...) ...)  =
     (g1 &&: g2 &&: ... )  ||:  (h1 &&: h2 &&: ...)  ||:  ...
(condI (g1 g2 ...) (h1 h2 ...) ...)  =
     (g1 &&: g2 &&: ... )  ||/  (h1 &&: h2 &&: ...)  ||/  ...

因此在 Haskell 中不需要任何特殊的“语法”,简单的二进制中缀运算符就足够了。任何组合都可以在任何地方使用,根据需要使用&&/ 而不是&&:。但另一方面,condI 也可以实现为接受要实现的目标集合(列表、树等)的函数,这将使用一些聪明的策略来挑选它们最可能或最需要等,而不仅仅是像||/ 运算符(或本书的ifI)中的简单二进制交替。

接下来,本书的condA可以由两个新的运算符~~>||~一起工作来建模。我们可以以自然的方式使用它们,例如

g1 ~~> g2 &&: ...  ||~  h1 ~~> h2 &&: ...  ||~  ...  ||~  gelse

可以直观地读作“IF g1 THEN g2 AND ... OR-ELSE IF h1 THEN ... OR-ELSE gelse”:

"IF-THEN" 目标组合是产生一个 "try" 目标,该目标必须与失败继续目标一起调用:

  (15)  (g ~~> h) f x  =  case g x of [] -> f x  ;  ys -> ys >>: h

"OR-ELSE" 目标组合 try 目标和简单目标只需将其 try 目标称为第二个失败时目标,因此仅此而已比用于操作数自动分组的便捷语法:

  (16)  (g ||~ f) x  =  g f x

OR-ELSE||~ 运算符的绑定能力低于“IF-THEN~~> 运算符并且也具有右关联性,而~~> 运算符的绑定能力仍低于&&:等等,上述示例的合理分组会自动生成为

(g1 ~~> (g2 &&: ...))  ||~  ( (h1 ~~> (h2 &&: ...))  ||~  (...  ||~  gelse ...) )

||~ 链中的最后一个目标因此必须是一个简单的目标。这实际上没有限制,因为 condA 形式的最后一个子句无论如何都等同于简单的“AND”-其目标组合(或者也可以使用简单的false)。

就是这样。如果我们愿意,我们甚至可以有更多类型的 try 目标,由不同类型的“IF”运算符表示:

在成功的子句中使用交替提要(如果书中有的话,可以称为 condAI 建模):

  (17)  (g ~~>/ h) f x  =  case g x of [] -> f x  ;  ys -> ys >>/ h

只使用成功的解流一次来产生cut效果,模拟condU

  (18)  (g ~~>! h) f x  =  case g x of [] -> f x  ;  (y:_) -> h y

所以,最后,本书condAcondU的重写规则很简单:

(condA (g1 g2 ...) (h1 h2 ...) ...)  = 
      g1 ~~> g2 &&: ...  ||~  h1 ~~> h2 &&: ...  ||~  ... 

(condU (g1 g2 ...) (h1 h2 ...) ...)  = 
      g1 ~~>! g2 &&: ...  ||~  h1 ~~>! h2 &&: ...  ||~  ... 

(*) 即:

简单并列就是柯里化函数应用 f a b c =~= (((f a) b) c) =~= f(a, b, c) (\ a -> b )lambda 函数, (lambda (a) b) foo x = yfoo = (\ x -> y ) 的快捷方式 a @@ b = y(@@) a b = y 的快捷方式,定义了中缀运算符@@ 括号 ( ) 仅用于分组 [] 是空列表, : 表示 cons -- 都作为构造函数(lazy,因为整个语言都是lazy,即按需调用 ),在定义中=右边;作为解构模式,在左边(或在模式匹配case 表达式中)。

【讨论】:

5 向上箭头堆栈溢出不值得此响应。我希望你把它变成一篇博文或其他东西。 作为后续:是 COND 吗? == 或者?和所有? == 和? ? @user1383359 或多或少。 CONDe 是 Prolog 的 OR(析取),ALL - Prolog 的 AND(析取)。 CONDi、ALLi 是子目标解决方案合并顺序的一种变体,试图将非生产性行为的预防提前内置到系统中。【参考方案2】:

Reasoned Schemer 涵盖 conda(软剪辑)和 condu(承诺选择)。您还可以在 William Byrd 出色的 dissertation on miniKanren 中找到对他们行为的解释。您已将此帖子标记为关于 core.logic。需要明确的是,core.logic 是基于 miniKanren 的更新版本,而不是 The Reasoned Schemer 中介绍的版本。 miniKanren 总是交错分离目标 - condi 并且交错变体不再存在。 conde condi 现在。

【讨论】:

FWIW 今天阅读的人们,The Reasoned Schemer 的第二版现在默认使用交错的 conde。【参考方案3】:

例如,使用 core.logic:

conde 将运行每个组,如果至少一个组成功则成功,并返回所有成功组的所有结果。

user>  (run* [w q]
                (conde [u#]
                       [(or* [(== w 1) (== w 2)])
                        (== q :first)]
                       [(== q :second)]))
([_0 :second] [1 :first] [2 :first])

conda 和 condu:都将在第一个成功组后停止(从上到下)

conda 仅返回第一个成功组的所有结果。

user> (run* [w q]
                (conda [u#]
                       [(or* [(== w 1) (== w 2)])
                        (== q :first)]
                       [(== q :second)]))
([1 :first] [2 :first])

condu 只返回第一个成功组的结果。

user> (run* [w q]
                (condu [u#]
                       [(or* [(== w 1) (== w 2)])
                        (== q :first)]
                       [(== q :second)]))
([1 :first])

不知道 condi 做了什么。

【讨论】:

不是一个正式的定义,但它可能会有所帮助 正如 dnolen 在他的回答中所写,core.logic 的 conde 是本书的 condi。本书的 conde 按顺序处理其子目标。如果第一个连续的子目标产生了无限个答案流,则组合的解决方案流中不会出现其他成功的子目标的解决方案。本书的 condi 通过交错流来解决这个问题。 太棒了!现在我明白了。什么时候没有赶上流/交错部分。从现在开始重新访问 core.logic src【参考方案4】:

根据 ISO Prolog 核心标准,控制结构,例如 (,)/2、(;)/2 和 (->)/2 是透明的。 (*->)/2 在 ISO Prolog 核心标准中找不到,但通常 Prolog 系统实现它也很透明。

这意味着无法翻译:

once(A) *-> B;C

进入A, !, B; C。因为后者可能嵌入在其他控制结构中,如果它们之间存在分离,这些选择点也会被切掉。另一方面,似乎是合理的,将其视为A -> B; C

简称为 ISO Prolog 核心标准if-then-else。如此定义的剪切行为例如对于打破重复循环很有用,而不会引发异常。通常的编程模式使用 if-then-else 更难归档。

【讨论】:

您可能是指像(X=1;X=2),(Y=1,!,Z=1 ; Z=2) 这样的目标只产生一个结果,而(X=1;X=2),(Y=1 -> Z=1 ; Z=2)(X=1;X=2),(once(Y=1) *-> Z=1 ; Z=2) 都产生两个结果?但是这本书一开始就没有!->。它所拥有的只是这些流操作。 感谢您的链接。我的回答不是关于 Prolog 的翻译。其中的 Haskell 代码是对 book 中的 Scheme 代码的翻译。并且由于本书的语言没有删减,我认为这两个 Prolog sn-ps 也是将 condacondu 正确翻译成 Prolog(即使它根本不是答案的目标;只是一个例子)。级联*-> 应该不是问题,对吧?即使在 Prolog 中,这也是关于括号问题的,它总是可以通过定义辅助谓词来修复/明确,以防止剪切泄漏。 (相对而言,我假设很少有图书读者 / minikanren 用户 / 知道 Prolog ......即使是 Haskell 在当时也是一个前卫的选择,但它已经变得更加流行了这么多年,而且代码很简单,基本都是伪代码)。

以上是关于康达, 康迪, 康德, 康杜的主要内容,如果未能解决你的问题,请参考以下文章

如何卸载迷你康达? Python

康达激活不起作用?

text 康达

markdown 康达相关指令

sh 康达

sh 康达