作为学校项目,我正在研究向后链接引擎。 到现在为止,我大部分时间都使用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'
除非事实已经存在于已知事实列表中,否则我们的想法是要有某种图,其中Fact节点指向Rule节点。
但是,在这里我遇到了定义实际事实和规则的问题。
做类似
let fact_e = ("E", [Fact ("C", [(Operation (Fact ("A", [])) (||) (Fact ("B", [])))])])
用ghci表示规则
C => E
A || B => C
那行得通。但是我真的看不到要以编程方式构建这些规则的方向。此外,我看不到如何使用该方案处理循环规则(例如,添加规则E => A
)。
我已经看到,可以使用Haskell Wiki上的“打结”技巧在haskell中定义自引用数据结构的方法,但是我不知道我应该(甚至即使)将其应用于本案。
我的问题本质上是,我是朝着正确的方向前进,还是采用这种方法使它完全落后?
PS:在我看来,我的代码也不如应有的简洁(绕过[Label]列表,重复eVal_rule proved
多次...),但是我真的不知道要么以另一种方式来做。
答案 0 :(得分:2)
想法是首先将规则解析为 not 自引用的中间表示形式。例如,给定表示形式:
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)
这也许值得解释。我们通过简单地应用knowledge
来创建learn1
,以将原始程序中每次出现的非自引用Rule_P
转换为知识库中的自引用Rule
。函数learn1
以明显的递归方式执行此操作,并通过在{{1}的正文中查找(Fact_P
)标签来“结”每个remember
},我们正在定义中。
无论如何,要向自己证明它是自我参照的,可以在GHCi中使用它:
knowledge
当然可以尝试
> 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'
会一直循环直到内存用完为止,因为它会尝试(未成功)从C从E到C证明E,等等,因此您需要添加一些逻辑来中止循环证明。