【问题标题】:Emulating Prolog backtracking in F#在 F# 中模拟 Prolog 回溯
【发布时间】:2010-12-17 23:30:43
【问题描述】:

我目前参与的项目是开发一个应用程序,该应用程序能够考虑一组节点和连接,并找到两个节点之间的最短路径(一个常见且众所周知的问题)(在允许的连接上)。好吧,我真的不必从零开始构建应用程序,而只需要在 f# 中“转换”一个 Prolog 预先存在的应用程序。 我想了一下,最后问了自己一个问题:“与其开发一个特殊用途的解决方案并实现专门针对这个问题的新算法,我是否可以创建一个能够接受像 Prolog 这样的事实并使用它们来进行查询或其他事情的程序类似吗?”。

通过这样做,我将创建一组事实(如在 Prolog 中),然后使用它们进行查询。 所以,现在考虑到这个新问题(在 F# 中转换 Prolog),我需要找到一种方法来创建这样的事实:

myfact1(el1, el2,..., eln).
myfact2(el1, el2,..., elm).
...
myfactk(el1, el2,..., elp).

到类似语法的东西,例如:

fact factname1: el1 el2 ... eln;
fact factname2: el1 el2 ... elm;
...
fact factnamek: el1 el2 ... elp;

我知道 F# 非常适合解析,所以我认为解析 this 可能不会有问题。

好的!现在它已经被解析了,我应该定义一个算法,在解析代码时,将所有事实存储在某种知识中(只不过是一张表)。为了建立所有需要的关联。

例如,一个解决方案可能是一个考虑所有关联的哈希表

factname1 -> el1
factname1 -> el2
...
factname1 -> eln
factname2 -> el1
factnale2 -> el2
...
factname2 -> elm
factname3 -> el1
...
...
factnamek -> el1
factnamek -> el2
...
factnamek -> elp

通过这样做,我将始终能够解决查询。 例如考虑以下 Prolog 事实

mother(A, B) % This means that A is mother of B
mother(C, D)

在 F# 中我创建

事实母亲:A B; 事实母亲:C D;

我的哈希表是:

母亲-> A |乙 母亲-> C | D

第一个列是事实名称,第二个是值(这里是一个元组)。

如果我要搜索:“谁是B的母亲”-->我搜索母亲,寻找价值,我找到B,我在元组中寻找A!

嗯,它似乎工作。 但事实很容易实现。规则呢? 例如规则父母:

parents(A, B, C) :- mother(A, C), father (B, C)

在 F# 中使用我的语法?好吧,我想出了这个主意:

rule parents: A, B, C => mother A C and father B C

当我的解析器遇到规则时,它应该怎么做?我想像我一样在表中创建某种记录,然后能够进行查询以指定主题并获取其父母或指定父亲并获取所有儿子等等...... 你会怎么做?

【问题讨论】:

    标签: parsing f# prolog language-design pattern-matching


    【解决方案1】:

    最近有was a similar question关于将类似Prolog的程序集成到F#中。

    F# 没有任何内置支持基于回溯执行搜索(如 Prolog)。您基本上有两种选择:

    • 自己使用递归和编码回溯在 F# 中重新实现算法。
    • 实现 Prolog 解释器并使用一些有区别的联合来表示事实。

    要实现最短路径搜索,我可能会直接在 F# 中实现算法(使用函数式编程会很方便,使用 Prolog 没有特别的理由)。

    如果你想实现一个解释器,你可能会使用一个有区别的联合,它允许你用这样的父母重写你的例子:

    type Var = Var of string
    type Expression = 
      | Binary of string * Expression * Expression
      | Fact of string * Expression list
      | Ref of Var
    type Rule =
      | Rule of string * Var list * Expression
    
    /// Simplified syntax for writing And
    let And(a, b) = Binary("and", a, b)
    
    let a, b, c = Var("A"), Var("B"), Var("C")
    Rule("parents", [a; b; c], 
      And(Fact("mother", [Ref a; Ref c]), Fact("father", [Ref b; Ref c])))
    

    【讨论】:

    • 感谢 Petricek 先生的回答。请只做一件事......您能否编辑您的答案并发布您将用于创建歧视工会的代码?非常感谢。
    • @Andry:是的,我为变量、表达式和规则添加了一个简单的类型声明。
    • 非常感谢 Petricek 先生 :)
    • 啊,我注意到一件事:类型 Rule 似乎是一个有区别的联合,但它有一个与类型同名的“成员”......它是一些递归联合定义吗?要不然是啥? (如你所见,我在这里是个新手......)
    • @Andry:不,这不是一个技巧 - 只是一个只有一个案例的有区别的联合(名称可以是其他任何东西)。我们可以为VarRule 使用其他类型(例如记录或F# 对象),但我使用了有区别的联合,因为它使模式匹配更容易。例如,如果您正在编写函数来返回规则的变量数,则可以编写 let numVars (Rule(_, args, _)) = args.Length
    【解决方案2】:

    一个很好的起点是查看在 F# 中实现 Prolog 风格的统一算法。良好的伪代码或 LISP 实现可以在许多通用人工智能中找到。教科书或网上。你可以从那个向外工作到回溯和语法。统一算法在 F# 等功能丰富的语言中实现起来应该相当简单(尽管可能有点晦涩难懂)。

    【讨论】:

    • 感谢您的建议...我很抱歉这个问题...什么是统一算法?我想这可能是有用的,所以请原谅我的坏知识......
    【解决方案3】:

    曾几何时,我认识一个写了EDSL for Prolog in C++ 的人。他一直想为 F# 做同样的事情,但一直没有找到时间。他似乎记得基本的 prolog 统一和回溯算法非常简单(也许是流行的 Scheme 文本的最后一章中的练习?)并希望有人能打败他,因为他正在度假。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-06
      • 1970-01-01
      相关资源
      最近更新 更多