我正在阅读Reasoned Schemer。
我对如何conde
工作有一些直觉。
但是,我找不到///做什么的conde
正式定义conda
。condu
condi
我知道https://www.cs.indiana.edu/~webyrd/但这似乎有例子而不是定义。
某处有conde
, conda
, condi
,的正式定义吗?condu
我正在阅读Reasoned Schemer。
我对如何conde
工作有一些直觉。
但是,我找不到///做什么的conde
正式定义conda
。condu
condi
我知道https://www.cs.indiana.edu/~webyrd/但这似乎有例子而不是定义。
某处有conde
, conda
, condi
,的正式定义吗?condu
用 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)
(分别带有“或”的;
意思和“和”的意思,即目标的分离和结合)。,
在condA
中,如果目标A
成功,则将所有解决方案传递给第一个子句,并且不尝试B
替代子句。C
In 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 ...) (...) ))
为了得到最终的condE
andcondI
的翻译,没有必要实现本书的ifE
and ifI
,因为它们进一步简化为简单的运算符组合,所有运算符都被认为是右结合的:
(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
" 目标组合是产生一个“尝试”目标,必须用失败-继续目标来调用它:
(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
form 的最后一个子句无论如何都等同于简单AND
的 " " - 其目标的组合(或者false
也可以使用简单的)。
就这样。如果我们愿意,我们甚至可以有更多类型的try目标,由不同类型的 " " 运算符表示:IF
condAI
在成功的子句中使用交替提要(如果书中有一个可以称为的模型):
(17) (g ~~>/ h) f x = case g x of [] -> f x ; ys -> ys >>/ h
仅使用一次成功的解决方案流来产生剪切效果,以建模condU
:
(18) (g ~~>! h) f x = case g x of [] -> f x ; (y:_) -> h y
condA
因此,最后,condU
本书的重写规则很简单:
(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 = y
是捷径foo = (\ x -> y )
a @@ b = y
(@@) a b = y
是中缀运算符定义的快捷方式@@
(
)
仅用于分组[]
是空列表,并且:
表示cons - 作为构造函数(惰性,因为整个语言都是惰性的,即按需调用),在定义的右侧=
;并作为解构模式,在左侧(或在模式匹配case
表达式中)。Reasoned Schemer 涵盖conda(软切)和condu(承诺选择)。您还可以在 William Byrd关于 miniKanren 的优秀论文中找到对他们行为的解释。您已将此帖子标记为关于 core.logic。需要明确的是,core.logic 是基于 miniKanren 的更新版本,而不是 The Reasoned Schemer 中介绍的版本。miniKanren 总是交错分离目标 - condi和交错变体不再存在。conde现在 是 condi。
例如,使用 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 做了什么。
根据 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 归档。