On Exercise(或entry?)57,我只是不了解逻辑是如何流动的。问题是:给定
(define teacupo
(lambda (x)
(conde
((= tea x ) #s)
((= cup x ) #s)
(else #u))))
其中' ='实际上是三栏统一(?)运算符。运行以下内容:
(run* (r)
(fresh (x y)
(conde
((teacupo x) (= #t y) #s)
((= #f x) (= #t y))
(else #u)
(= (cons x (cons y ())) r)))
这本书给出了答案:
((tea #t) (cup #t) (#f #t))
我原以为答案应该是:
(((tea cup) #t) (#f #t))
我的理由是' x' in(teacupo x)应首先通过其所有解决方案,并统一到其所有解决方案的列表中。但似乎茶杯一次只放弃其中一种解决方案。这让我很困惑,因为我对conde的解释是,使用它给出的规则,你应该通过conde的行,并且在一行成功之后,假装它失败了,刷新变量并找到成功的下一行。考虑到解决方案的工作方式,似乎代码中的conde会回到成功的行,然后迫使teaupo conde失败并放弃下一个可能的值。再说一遍,我原以为茶杯解决方案会放弃列表中的所有conde解决方案,然后继续进行外部conde调用。任何人都可以向我提供指导,说明为什么它按照书中提供的方式工作而不是我推理的方式?
答案 0 :(得分:4)
我的理由是' x'在(茶杯x)应该有它的conde 首先完成所有解决方案,并统一到所有解决方案的列表中 它的解决方案。
变量x
一次统一为一个值。
表单(run* (x) <goals>)
会收集符合目标的x
值。
> (run* (x)
(teacupo x))
'(tea cup)
在
(conde
((teacupo x) (== #t y))
((== #f x) (== #t y)))
有两种方法可以取得成功:要么达到目标(teacupo x)
而x
属于tea
或cup
- 或者目标{{1符合,(== #f x)
(统一到)x
。
简而言之,#f
会在run*
一个可能的值中运行,以便收集满足所有目标的值。这意味着x
一次统一为一个值。
一个更简单的例子:
x
想要在DrRacket中尝试片段的人的完整代码:
> (run* (x)
(fresh (y)
(== y 10)
(conde
[(== x 1) (== y 10)]
[(== x 2) (== y 20)]
[(== x 3) (== y 10)])))
'(1 3)
答案 1 :(得分:3)
(teacupo x)
表示“成功两次:x
与tea
统一,第二次与x
统一cup
< / em>的。然后,
(conde
((teacupo x) (= #t y) #s)
((= #f x) (= #t y)) ; you had a typo here
(else #u)
表示,
(teacupo x)
生成的每个解决方案,同时y
与#t
统一并成功;还有 (= #f x)
生成的每个解决方案,同时y
与#t
统一并成功;还有 因此,x
中的每个(tea cup)
与y
中的(#t)
配对,x
中的(#f)
与y
配对在(#t)
中{1}},形成r
;然后报告r
,即收集到解决方案的最终结果列表中,并提供( (tea #t) (cup #t) (#f #t) )
。
“似乎茶杯一次只放弃其中一种解决方案。”
是的,这在概念上是完全正确的。
“一行成功后,假装失败,刷新变量并找到成功的下一行。”
是的,但如果条件(或后续目标)成功多次,则每一行都可以多次成功。
“似乎代码中的conde回到了成功的行,然后迫使teaupo conde失败并放弃下一个可能的值。”
它实际上准备提前生产它们(但是作为流,而不是列表),然后每个单独处理,通过整个后续步骤链进行,直到成功到达最后一步,或链被打破,被#u
或其他失败的目标缩短。因此,当前一个处理完成时,尝试下一个。
在伪代码中:
for each x in (tea cup):
for each y in (#t): ; does _not_ introduce separate scope for `y`;
collect (x y) ; `x` and `y` belong to the same logical scope
for each x in (#f): ; so if `x` is used in the nested `for` too,
for each y in (#t): ; its new value must be compatible with the
collect (x y) ; one known in the outer `for`, or else
for each _ in (): ; it will be rejected (x can't be two different
collect (x y) ; things at the same time)
至于为什么以这种方式工作,我可以指向another answer of mine,这可能会有所帮助(尽管它不使用Scheme语法)。
使用它,作为一个例子,我们可以将你的测试写成一个Haskell代码,它实际上在功能上等同于我认为的书的代码(当然在这个特定的情况下),
data Val = Fresh | B Bool | S String | L [Val] deriving Show
type Store = [(String,Val)]
teacupo x = unify x (S "tea") &&: true -- ((= tea x ) #s)
||: unify x (S "cup") &&: true -- ((= cup x ) #s)
||: false -- (else #u)
run = [[("r", Fresh)]] -- (run* (r) ......
>>: (\s -> [ s ++: [("x", Fresh), ("y", Fresh)] ]) -- (fresh (x,y)
>>: -- (conde
( teacupo "x" &&: unify "y" (B True)
&&: true -- ((teacupo x) (= #t y) #s)
||: unify "x" (B False) &&: unify "y" (B True) -- ((= #f x) (= #t y))
||: false -- (else #u)
)
&&: project ["x", "y"] (unify "r" . L) -- (= r (list x y))
>>:
reporting ["r"] -- ...... )
reporting names store = [[a | a@(n,_) <- store, elem n names]]
只有极少的实现,足以使上面的代码工作,
project vars kont store =
kont [val | var <- vars, (Just val) <- [lookup var store]] store
unify :: String -> Val -> Store -> [Store]
unify sym val store =
let
(Just v) = (lookup sym store)
in
case (val_unify v val) of
Just newval -> [replace_val sym newval store] -- [updated store], if unifies
Nothing -> [] -- couldn't unify - reject it
val_unify v Fresh = Just v -- barely working,
val_unify Fresh v = Just v -- initial
val_unify (B v) (B u) | v == u = Just (B v) -- implementation
| otherwise = Nothing
val_unify (S v) (S u) | v == u = Just (S v)
| otherwise = Nothing
val_unify _ _ = Nothing
replace_val s n ((a,b):c) | s == a = (a,n) : c
| otherwise = (a,b) : replace_val s n c
产生输出
*主&GT;跑 [[(“r”,L [S“tea”,B True])],[(“r”,L [S“cup”,B True])],[(“r”,L [B False, B真])]]
如果我们将翻译的conde
表达式中的第二行更改为
||: unify "x" (B False) &&: unify "x" (B True) -- ((= #f x) (= #t x))
我们确实只得到两个结果,
*主&GT;跑 [[(“r”,L [S“tea”,B True])],[(“r”,L [S“cup”,B True])]]