【问题标题】:Rules and Facts (cyclic ?) definition in an inference engine推理引擎中的规则和事实(循环?)定义
【发布时间】:2019-06-20 15:20:32
【问题描述】:

我正在开发一个反向链接引擎作为一个学校项目。 到目前为止,我主要是用 C 语言完成项目,所以我决定为那个项目尝试 Haskell。我已经阅读了 LYAH 以开始并开始在我的推理引擎中实现规则和事实的表示。 到目前为止,这就是我得到的

module Inference () where

type Op = Bool -> Bool -> Bool
type Label = String
type Fact = (Label, [Rule])
data Rule = Operation Rule Op Rule
          | Fact Fact

eval_fact:: [Label] -> Fact -> Bool
eval_fact proved (label,rules) = label `elem` proved || any (eval_rule proved) rules

eval_rule:: [Label] -> Rule -> Bool
eval_rule proved (Fact x) = eval_fact proved x
eval_rule proved (Operation r op r') =  eval_rule proved r `op` eval_rule proved r'

这个想法是要有某种图表,其中事实节点指向规则节点,除非事实已经在已知事实列表中。

但是,在这里我遇到了定义我的实际事实和规则的问题。

做类似的事情

let fact_e = ("E", [Fact ("C", [(Operation (Fact ("A", [])) (||) (Fact ("B", [])))])])

在 ghci 中为了表示规则

C => E
A || B => C

这行得通。但是我真的不知道以编程方式构建这些规则的方向。此外,我看不到如何使用该方案处理循环规则(例如添加规则E => A)。

我已经看到有一些方法可以在 Haskell 中使用 Haskell wiki 上称为“打结”的技巧来定义自引用数据结构,但我不知道应该如何(或什至)在本案。

我的问题本质上是,我是在朝着正确的方向前进,还是用这种方法完全倒退了?

PS:在我看来,我的代码也没有应有的简洁(传递 [Label] 列表,重复 eVal_rule proved 很多次......),但我也不知道如何用另一种方式来做。

【问题讨论】:

  • 您不是已经以编程方式构建规则了吗?也就是说,您已经使用编程指定了诸如fact_e 之类的规则。对于循环规则,您应该能够将它们全部放入一个let 块(例如let x1 = val1 <newline> x2 = val2 <newline> x3 = val3 in _),并让惰性自动整理循环。至于您的 P.S.:为了传递 [Label],请尝试了解“Reader monad”。如果你使用Readereval_rule proved 的重复应该会更好一些。
  • 我的意思是我不知道如何动态地执行它(例如通过从文件中解析),而不是在代码中静态地写下来。关于let,这就是我正在尝试的,谢谢:)。
  • 对于动态生成Facts 的列表,您应该能够像任何其他数据结构一样执行此操作。例如,如果您有C => E,您可以将其拆分为["C", "=>", "E"],然后编写函数将列表中的各个项目转换为Operations 和其他Facts,然后使用适当的方法将它们组合在一起构造函数。如果不确切知道自己坚持什么,很难说除此之外的其他内容。您是否正在为解析 String 或将解析后的 String 转换为 Fact 而苦苦挣扎?
  • 我的麻烦更多在于规则的可能自我引用。将我的源代码解析为我的表示并不难,但翻译成自我引用的表达有点困难。我的答案来自@K。 A. Buhr 正是我想要做的。

标签: haskell recursive-datastructures cyclic-reference inference-engine


【解决方案1】:

我们的想法是首先将规则解析为一个自引用的中间表示。例如,给定表示:

type Program = [(Label, [Rule_P])]
data Rule_P = Operation_P Rule_P Op Rule_P | Fact_P Label

然后是规则集:

C => E
A || B => C
E => A
F => E

将被解析,按蕴含目标收集,并表示为:

prog1 :: Program
prog1 = [ ("E", [ Fact_P "C"                                       -- C => E
                , Fact_P "F" ])                                    -- F => E
        , ("C", [ Operation_P (Fact_P "A") (||) (Fact_P "B") ])    -- A || B => C
        , ("A", [ Fact_P "E" ]) ]                                  -- E => A

然后,将其转换为循环自引用的知识库(使用您原来的 Fact 类型):

type Knowledge = [Fact]

你像这样打结:

learn :: Program -> Knowledge
learn program = knowledge

  where

    knowledge :: [Fact]
    knowledge = [ (target, map learn1 rules_p) | (target, rules_p) <- program ]

    remember lbl = fromJust (find ((==lbl) . fst) knowledge)

    learn1 :: Rule_P -> Rule
    learn1 (Fact_P lbl) = Fact (remember lbl)
    learn1 (Operation_P rule1 op rule2) = Operation (learn1 rule1) op (learn1 rule2)

这也许值得一些解释。我们通过简单地应用learn1 来创建knowledge,将原始程序中每次出现的非自引用Rule_P 转换为知识库中的自引用Rule。函数learn1 以明显的递归方式执行此操作,它通过查找(remembering)knowledge 正文中我们位于中间的标签,在每个 Fact_P 处“打结”的定义。

无论如何,为了向自己证明它是自我参照的,你可以在 GHCi 中使用它:

> know1 = learn prog1
> Just [Operation factA _ _] = lookup "C" know1
> Fact ("A", [factE]) = factA
> Fact ("E", [factC, _]) = factE
> Fact ("C", [Operation factA' _ _]) = factC
> Fact ("A", [factE']) = factA'

当然,尝试:

> eval_fact [] $ fromJust $ find ((=="E").fst) (learn prog1)

将循环直到内存用完,因为它试图(不成功地)从 E 中从 A 中从 C 中证明 E 等等,因此您需要添加一些逻辑来中止循环证明。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-18
    • 2013-12-24
    • 1970-01-01
    • 2010-10-23
    相关资源
    最近更新 更多