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

How should I handle repeated updating in logical programming?

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

我感兴趣的例子是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 将 h 与 h1 进行变异,h1 是使用 find-s 算法 (find-so) 从 h0 和 x 训练示例计算出的下一个假设。

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

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

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

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

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

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

模拟这些状态变化的声明式解决方案是根据状态之间的关系

来思考

例如,当您使用 H0 计算 next 假设 H1 时,您可以将其表示为 H0 之间的关系] 和 H1,以及可能的其他参数。考虑像这样的谓词:

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

请注意,此类谓词可以 read 并且——理想情况下——运行 在各个方向!

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

H0H1H2 → … → H

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

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