理性计划者描述了如何使用miniKanren,它类似于Prolog,但是是类似Lisp的语言的库。这本书的“第一条诫命”是这样的:
转换值为布尔值的函数 转化为以价值为目标的功能,替换cond 与conde和unsest每个问题和答案。 将答案替换为#s来取消答案#t(或#f) (或#u)。
除非通过几个大致等效的示例,否则它们实际上并没有定义嵌套。最清楚的是:取消嵌套会将您从_privatekey_login
移至
(list? (cdr l))
我不明白为什么需要嵌套。例如,对于上述目标,为什么tp写(fresh (d)
(cdro l d)
(listo d))
还不够?
如here所述,我跑了(listo (cdr l))
,然后定义了一些缺失的部分。
这里是raco pkg install minikanren
的定义及其使用的所有内容,除了listo
库或Racket的前奏中定义的内容。
minikanren
答案 0 :(得分:2)
术语un-nesting
表示修改结构的层次结构或删除方括号。
刚刚找到了书The Reasoned Schemer和相关的GitHub page,后者也对其进行了定义:
将
(car (cdr l))
转换为(cdro l v)
和(caro v r)
的过程称为取消嵌套…。认识到嵌套和[CPS]之间的相似性。
答案 1 :(得分:1)
免责声明,我不知道计划,我大约占本书的1/5。
我认为这是必要的,因为您要替换的函数的输入奇偶校验(采用的参数数量)通常低于您要替换的函数。您正在对函数进行重构,以将输出放入传递给它的变量中,而不是将其作为输出返回。为什么需要用逻辑等效项替换每个功能?我不太确定,
(编辑,我阅读了更多示例的上下文...) 如果我们以您提供的示例为例,则标准版本如果传递了我认为的非列表,则会引发错误,这与返回失败不同。因此,如果传递的不是列表,而不是从运行中获取(),则会引发一个不同的异常。 我意识到还有另一个差异,一个更重要的差异。您的示例来自listo的定义,并且listo不仅检查是否可以将某项列表。如果给定一个未绑定的变量,它可以用来生成一个无限列表。这样做是因为首先conde中的第一个元素将成功,因此您将获得()。之后,pairo l将使第一个元素(reified_1)成功,第二个元素返回listo,此内部listo将首先返回(),就像外部listo的第一个结果一样,因此得到(reified_1。())和这可以无限持续下去。之所以有效,是因为您(cdro l d)带有一个新的d,从而允许d绑定到由递归调用listo设置的变量。如果不通过嵌套创建d变量,就无法做到这一点。
解释奇偶点:
cdr(x) # get the rest elements of x
cdro(a x) # you "pass in" the equivalent to the output element of the first function into this logical version.
这意味着如果您有b = cdr(cdr(x)) 您需要创建一个变量来保存中间值,例如:
fresh(a)
cdro(a, x)
fresh(b)
cdro(b, a)
看到差异/原因了吗?您正在传递您的输出。因此,您不能“嵌套”所有内容位于嵌套表达式中的位置,需要将其分成几行以留出空间来分配新变量。
为什么一定要这样?我正在考虑通过重载基于奇偶校验的函数是否可以避免很多不必要的事情。就像我在下面写的(在python中,假设已经定义了caro)。关键是,我认为它没有用,因为绑定到列表的第一个元素的变量是新变量,在其他任何地方均未引用,因此对于应用任何其他约束可能没有用。目标的目的是返回满足它们的绑定集,但是这些绑定必须位于已经定义的变量上,否则它们将无用。
class UNDEF:
pass
def cdronew(a, b=UNDEF):
if b is UNDEF:
# assume that we need to fresh a variable and use that for our output
b = fresh('b')
# not a typo in order, we're assuming we wanted the first val of the list
# here is the problem, we'll bind b but noone outside this function knows about it so that binding is useless!
return cdro(b, a)
else:
return cdro(a, b)
答案 2 :(得分:0)
“嵌套”用于SSA转换,因为Prolog就是这样,因为它没有评估,并且所有状态更改和传递都必须明确进行。
SSA表单是执行有限形式的CPS转换后所获得的代码。简而言之,每个临时实体都被明确指定并命名。
(let* ((c (+ (sqr x) (sqr y)) )
(z (sqrt c)))
....
被转化为
(let* ((a (sqr x))
(b (sqr y))
(c (+ a b))
(z (sqrt c)))
....
类似地,在您编写时,Lisp代码(list? (cdr l))
变成了谓词
( L = [_ | D],
is_list( D ) )
在Prolog中,这是目标
(fresh (d)
(cdro l d) ; a relation "cdro" holds between `l` and `d`
(listo d)) ; a predicate listo holds for `d`
在miniKanren中。为什么?因为(cdr l)
是 Lisp 函数,它返回一个值。但是在Prolog中没有评估,没有隐式返回的值,而是通过 predicate , relation 通过“设置”逻辑来显式地将它们存在。变量,它是该谓词的参数。