我应该如何处理逻辑编程中的重复更新?

Posted

技术标签:

【中文标题】我应该如何处理逻辑编程中的重复更新?【英文标题】:How should I handle repeated updating in logical programming? 【发布时间】:2017-03-03 05:51:09 【问题描述】:

特别是我正在尝试确定解决以下类型问题的最佳方法:

我感兴趣的示例是 Mitchell 的机器学习书中的 find-s 算法,它被应用于 4 个训练示例。

基本思想是对于每个训练示例 x 和假设 h,确定 h' 是否包含 x,使其更通用。对于训练集中的每个 x,我需要将 h 映射到 h'。我遇到的问题是如何用逻辑编程语言最好地解决这个问题。我正在使用 minikanren,它大致是嵌入在方案中的序言。

计算每个h'后,我需要再设置!它到一个全局变量 h,然后继续下一个训练示例 x。下面的代码是程序的主要部分。

(define h '(0 0 0 0 0 0))

(define seto
  (lambda (x)
    (project (x)
             (lambda (s) (set! h x) (succeed s)))))

(run* (q)
     (fresh (x h0 h1)
            (trainingo x)
            (== h h0)
            (find-so h0 x h1)
            (seto h1)
            (== h1 q)))

h 是全局变量,seto 使用 find-s 算法 (find-so) 从 h0 和 x 训练示例中计算出的下一个假设 h1 对 h 进行变异。

在序言中,它(我认为)相当于 assert('hypothesis'(H)) 在每个训练示例 X 之后(覆盖前一个)并调用 retract('假设'(H)) 在应用了所有训练示例之后。

我的问题是,这是否是解决此类问题的最佳方法(通过副作用)?

编辑: 我接受了@mat 答案和他的comment。总之,我需要将训练示例视为一个列表,并在该列表上使用前向递归,直到我到达空列表。如果我陷入困境,就是将训练示例作为回溯的一部分,同时寻找下一个假设,而不是将它们放在我可以重复到空的列表中。

【问题讨论】:

你需要解释为什么你想通过副作用来做到这一点。 @mat 的回答解释了为什么你不应该为此需要副作用。 顺便说一句,您可以在 Prolog 库代码中找到出于效率原因使用副作用的示例。见this code in SWI-Prolog's library(aggregate)。在任何情况下,您最终都会使用来自nb_* 系列的谓词,其中“nb”代表“不可回溯”。 我在@mat 回答中提到了这一点作为评论:“......我遇到的问题是在我得到下一个假设然后回溯到下一个训练示例之后——我失去了我的下一个假设. 如果可能的话,我不想使用副作用来解决这个问题。” 不要回溯。链接假设更新:h1 + x1 = h2; h2 + x2 = h3; ...; hn + xn = final_h 。 (另见@mat's comment)。 要添加到其他 cmets:您可以更改 Prolog 的默认深度优先搜索策略(通过证明树),如果这是您的问题所需要的,您应该这样做。在“Prolog 的工艺”中有一整章介绍了不同的搜索策略以及如何实现它们。 【参考方案1】:

您的建议可能看起来诱人:使用assertz/0retract/1 模拟全局更新。

但是,如果您这样做,会有主要缺点

使用全局状态会阻止您使用测试您的谓词孤立地。 使用破坏性更新会阻止您在更多方向使用谓词。 全局状态的微妙和隐含的依赖关系使您的谓词更改非常容易出错

模拟这些状态变化的声明性解决方案是考虑状态之间的关系

例如,当您使用H0 计算下一个 假设H1 时,您可以将其表示为H0H1 之间的关系,可能还有更多参数。想想这样的谓词:

hypothesis_next(H0, H1) algorithm_hypothesis_next(A, H0, H1) parameters_hypothesis_next(Ps, H0, H1)

请注意,此类谓词可以读取并且——理想情况下——在所有方向上运行

总的来说,在您的情况下,整个解决方案将被建模为一个假设序列

H0 → H1 → H2 → … &右箭头; H

有关更多信息和提示,请参阅密切相关的问题:

How to avoid using assert and retractall in Prolog to implement global (or state) variables

【讨论】:

谢谢!但是我遇到的问题是在我得到下一个假设然后回溯到下一个训练示例之后——我失去了下一个假设。如果可能的话,我不想使用副作用来解决这个问题。 解决方案是用前向递归代替回溯。假设您有训练示例Es 来完善您的假设H0。您可以使用如下谓词来描述这一点:examples_h0_h([], H, H).(说:如果没有示例,则最终假设与最初的假设相同)和examples_h0_h([E|Es], H0, H) :- hypothesis_next(E, H0, H1), examples_h0_h(Es, H1, H).,通过每个示例逐步完善假设。由于这种模式在 Prolog 中经常出现,因此有一个元谓词 foldl/4 用于此:foldl(hypothesis_next, Es, H0, H)

以上是关于我应该如何处理逻辑编程中的重复更新?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理消费过程中的重复消息?

Java:如何处理 servlet 中的多个会话 [重复]

如何处理片段中的onClick [重复]

如何处理 Postgresql 查询中的单引号 [重复]

如何处理 lambda 表达式中的异常 [重复]

我应该如何处理包中的头文件?