当我想阅读逻辑编程时,时下我总是会遇到两种"主要"方法:
-
miniKanren,在The Reasoned Schemer中引入的一种迷你语言,由于core.logic的缘故,目前很受欢迎。
-
Prolog,第一种"大"逻辑编程语言。
我现在感兴趣的是:两者之间的主要技术区别是什么? 它们在方法和实现上是否非常相似,还是对逻辑编程采用完全不同的方法? 它们来自哪些数学分支?其理论基础是什么?
首先,请允许我赞美您的pw0n1e图标。
好。
这是一个棘手的问题,主要是因为miniKanren和Prolog都有很多变体。 miniKanren和Prolog实际上是语言家族,这使得很难比较它们的功能,甚至很难比较它们在实践中的用法。因此,请谨慎处理我要说的一切:如果我说Prolog使用深度优先搜索,请注意,许多Prolog实现都支持其他搜索策略,并且其他搜索策略也可以在meta处进行编码。 -解释器级别。 miniKanren和Prolog仍然有不同的设计理念,并做出了不同的取舍。
好。
Prolog是用于符号人工智能编程的两种经典语言之一(另一种经典语言是Lisp)。 Prolog擅长实现基于符号规则的系统,其中声明性知识以一阶逻辑编码。该语言针对这些类型的应用程序的表达性和效率进行了优化,有时会牺牲逻辑上的纯正性。例如,默认情况下,Prolog不统一使用"出现检查"。从数学/逻辑的角度来看,此版本的统一是不正确的。但是,发生检查很昂贵,并且在大多数情况下,缺少发生检查不是问题。这是一个非常务实的设计决策,Prolog使用深度优先搜索以及使用cut(!)来控制回溯也是如此。我确信这些决定在1970年代的硬件上运行时绝对必要,而今天在处理大问题以及处理巨大(通常是无限的!)搜索空间时非常有用。
好。
Prolog支持许多"额外逻辑"或"非逻辑"功能,包括剪切,assert和retract,用于使用is进行算术运算的变量投影等。这些功能中的许多功能使表达复杂的控制流程和操作Prolog的全局事实数据库变得更加容易。 Prolog的一个非常有趣的功能是Prolog代码本身存储在事实的全局数据库中,并且可以在运行时进行查询。这使得编写修改解释中的Prolog代码行为的元解释器变得微不足道。例如,可以使用更改搜索顺序的元解释器在Prolog中对广度优先搜索进行编码。这是一种非常强大的技术,在Prolog世界之外尚不为人所知。"序言的艺术"详细介绍了此技术。
好。
改进Prolog的实现已付出了巨大的努力,其中大多数基于Warren Abstract Machine(WAM)。 WAM使用一个副作用模型,在该模型中,将值破坏性地分配给逻辑变量,而这些副作用在回溯时将被撤消。通过扩展WAM的说明,可以将许多功能添加到Prolog中。这种方法的一个缺点是,如果没有对WAM的深入了解,可能很难阅读Prolog实施文件。另一方面,Prolog实现者具有讨论实现问题的通用模型。在并行的Prolog中进行了大量的研究,最终在1990年代的Andorra Prolog中达到顶峰。这些想法中至少有一些存在于Ciao Prolog中。 (Ciao Prolog充满了有趣的想法,其中许多远远超出了Prolog的标准。)
好。
Prolog具有漂亮的基于统一的"模式匹配"样式的语法,从而导致程序非常简洁。序言者喜欢他们的语法,就像Lispers喜欢他们的S表达式一样。 Prolog还具有大量的标准谓词库。由于使WAM快速运行的所有工程技术,因此有非常强大且成熟的Prolog实现。结果,许多大型的基于知识的系统已完全用Prolog编写。
好。
miniKanren被设计为一种最小的逻辑编程语言,具有一个小型,易于理解且易于破解的实现。 miniKanren最初嵌入在Scheme中,并在过去十年中已移植到其他数十种宿主语言中。最受欢迎的miniKanren实现是Clojure中的" core.logic",它现在具有许多类似于Prolog的扩展和许多优化。最近,miniKanren实现的核心得到了进一步简化,从而产生了一个名为" microKanren"的微型"微内核"。然后,可以在此microKanren核心之上实施miniKanren。将microKanren或miniKanren移植到新的宿主语言已成为学习miniKanren的程序员的标准练习。结果,大多数流行的高级语言至少具有一种miniKanren或microKanren实现。
好。
miniKanren和microKanren的标准实现不包含任何突变或其他副作用,唯一的例外是:某些miniKanren版本使用指针相等性来比较逻辑变量。我认为这是"良性效果",尽管许多实现甚至通过在实现中传递计数器来避免这种效果。也没有全局事实数据库。 miniKanren的实现哲学受到函数式编程的启发:应避免突变和影响,所有语言构造应遵循词汇范围。如果仔细看一下实现,您甚至可能会发现几个monad。搜索实现基于合并和处理惰性流,再次不使用任何突变。这些实现选择导致的权衡与Prolog不同。在Prolog中,变量查找是固定时间,但是回溯需要消除副作用。在miniKanren中,变量查找更为昂贵,但回溯是"免费的"。实际上,由于如何处理流,miniKanren中没有回溯。
好。
miniKanren实现的一个有趣的方面是,该代码本质上是线程安全的,并且-至少在理论上-可以并行化。当然,考虑到必须给每个线程或进程足够的工作来弥补并行化的开销,并行化代码而不降低速度并非易事。尽管如此,这还是miniKanren实施的一个领域,我希望它将得到更多关注和试验。
好。
miniKanren使用发生检查进行统一,并使用完整的交织搜索而不是深度优先搜索。交错搜索比深度优先搜索使用更多的内存,但是在深度优先搜索将永远发散/循环的某些情况下,可以找到答案。 miniKanren确实支持一些额外的逻辑运算符,例如conda,condu和project。 conda和condu可用于模拟Prolog的切割,而project可用于获取与逻辑变量关联的值。
好。
conda,condu和project-的存在-以及轻松修改搜索策略的能力-允许程序员使用miniKanren作为类似于Prolog的嵌入式语言。对于Clojure'core.logic'的用户而言尤其如此,其中包括许多类似于Prolog的扩展。 miniKanren的这种"实用"用法似乎占miniKanren在工业中的大多数使用。希望将基于知识的推理系统添加到用Clojure或Python或JavaScript编写的现有应用程序中的程序员通常对用Prolog重写整个应用程序不感兴趣。在Clojure或Python中嵌入小型逻辑编程语言更具吸引力。大概,嵌入式Prolog实现也可以达到此目的。我怀疑miniKanren由于其微小而纯净的内核实现以及自"推理的策划人"发布以来出现的讲座,博客文章,教程和其他教育材料而成为一种流行的嵌入式逻辑语言。
好。
除了将miniKanren用作与Prolog精神相似的实用嵌入式逻辑编程语言外,miniKanren还被用于"关系"编程中的研究。就是说,在编写程序时表现为数学关系而不是数学函数。例如,在Scheme中,append函数可以追加两个列表,并返回新列表:函数调用(append '(a b c) '(d e))返回列表(a b c d e)。但是,我们也可以将append视为三位关系而不是二元函数。然后,调用(appendo '(a b c) '(d e) Z)将逻辑变量Z与列表(a b c d e)关联。当然,当我们将逻辑变量放在其他位置时,事情会变得更加有趣。调用(appendo X '(d e) '(a b c d e))将X与(a b c)关联,而调用(appendo X Y '(a b c d e))将X和Y与列表对关联,这些列表对在添加时等于(a b c d e)。例如,X = (a b)和Y = (c d e)是一对这样的值。我们还可以编写(appendo X Y Z),它将产生无限多个列表X,Y和Z的三元组,以便将X附加到Y会生成Z。
好。
append的这种关系版本可以在Prolog中轻松表达,并且实际上在许多Prolog教程中都已显示。在实践中,更复杂的Prolog程序倾向于至少使用一些额外的逻辑功能,例如cut,这会限制将生成的程序视为关联的能力。相反,miniKanren专门设计为支持这种类型的关系编程。 miniKanren的最新版本支持符号约束求解(symbolo,numbero,absento,不等式约束,标称逻辑编程),以使将非平凡程序编写为关系变得更容易。在实践中,我从不使用miniKanren的任何逻辑外功能,而是将所有miniKanren程序编写为关系。最有趣的关系程序是Scheme子集的关系解释器。这些解释器具有许多有趣的功能,例如生成一百万个对列表(I love you)求值的Scheme程序,或琐碎生成quines(对自己求值的程序)。
好。
miniKanren进行了一些权衡以实现这种关系编程风格,这与Prolog进行的权衡非常不同。随着时间的流逝,miniKanren增加了更多的符号约束,实际上成为了一种面向符号的约束逻辑编程语言。在许多情况下,使用这些符号约束可以避免使用诸如condu和project之类的逻辑运算符。在其他情况下,这些符号约束是不够的。更好地支持符号约束是miniKanren研究的一个活跃领域,也是一个更广泛的问题,即如何编写更大和更复杂的程序作为关系。
好。
简而言之,miniKanren和Prolog都具有有趣的功能,实现和用法,我认为值得从这两种语言中学习思想。还有其他非常有趣的逻辑编程语言,例如Mercury,Curry和G?del,每种语言都有自己的逻辑编程语言。
好。
我将以一些miniKanren资源结尾:
好。
miniKanren主要网站:
http://minikanren.org/
好。
我对关系编程和miniKanren进行了一次采访,其中包括与Prolog的比较:
http://www.infoq.com/interviews/byrd-relational-programming-minikanren
好。
干杯,
好。
- 将
好。
好。
-
如果我现在被吓到了,不好意思。我刚刚开始了基于逻辑和基于约束的编程的第一个思想实验,这就是我得到的答复。 :)实际上,正如您在回答中提到的那样,我也很怀疑逻辑计算是实现为Monad的理想人选;事实证明,它更像是MonadPlus,而您的同事Friedman确实有论文和参考实现!因此,我正在阅读并开始使用它,对此有任何想法吗?
-
我怀疑您还会发现microKanren论文和代码有趣:webyrd.net/scheme-2013/papers/HemannMuKanren2013.pdf和github.com/jasonhemann/microKanren
-
另外,我每周一次在东部标准时间周日下午3点(格林尼治标准时间-5:00)组织有关Google Hangouts的miniKanren课程。如果您想加入我们,我总是在我的@webyrd Twitter帐户上发布该链接。先前录制的视频群聊位于:youtube.com/playlist?list=PLO4TbomOdn2cks2n5PvifialL8kQwt0aW
-
实际上,(define (mplus …) …) :)。我认为这是从05论文中衍生出来的吗?
-
我认为其搜索单子与05论文基本相同。另请参见由Seres和Spivey撰写的在Haskell中嵌入Prolog:spivey.oriel.ox.ac.uk/~mike/silvija/seres_haskell99.pdf
-
@ WilliamE.Byrd-很棒的答案!假设有一个,可以嵌入到C / C ++程序中的最佳miniKanren实现是什么?另外,miniKanren是否具有Prologs生成性质?就是说,在程序声明的当前关系给定的情况下,将变量留在表达式中的能力以及核心引擎是否会为该变量提供所有可能的值?
-
好问题,罗伯特。我还没有看到C或C ++的任何实现。现在应该可以将microKanren移植到C ++,因为它具有lambda。 C似乎比较棘手,因为它没有闭包。一个简单的miniKanren-to-C编译器可能比嵌入更有用。
-
在编写为关系的miniKanren程序中,您可以在查询中的任何位置放置逻辑变量,并且miniKanren会尝试使用合法值实例化这些变量。我通常会写我的关系以产生尽可能通用的答案。例如,我通常选择一个答案,即X是表示某个整数的未绑定逻辑变量,而不是绑定到某个逻辑变量X的无限整数流。
-
好吧,有(或至少曾经是)C编译器的Scheme,一个只有一个。所以我想您可以使用其中之一来在C上运行miniKanren
-
我希望在您的网站上看到这种比较,但是我也一直在学习Mercury,我想知道MiniKanren和Mercury之间的主要区别是什么?
-
@ WilliamE.Byrd Robert再次在这里。 miniKanren是否具有Prolog的世代特征?我指的是Prolog我最喜欢的方面之一,在该方面中,您可以使变量保持不接地状态,并且通过反复失败达到目标,可以使Prolog推理引擎为未接地变量提供所有可能的值。 MiniKanren有这种行为吗?
-
嘿罗伯特! miniKanren将在可能的情况下尝试给出最通用的答案,其中最新鲜的(未实例化)变量最通用。如果最普遍的答案确实是地线,那么将逻辑变量传递给miniKanren查询确实会生成所有可能的地线实例化。
-
顺便说一句,命名诸如appendo之类的功能而不只是append的原因是什么?
-
让代码更改解释器,然后恢复Lisp和Prolog中强大的先前解释器的声音,我想对此有所了解。 我想实现一个模棱两可的评估,尝试对您的表达式/或函数定义重新排序,以使其在错误时起作用。 这将帮助我避免在编码时出现愚蠢的错误。 同样,最好在minikanren上实现一个类似于google的良好搜索数据库,以从庞大的开源代码数据库中查找代码,并通过查看代码并告诉您其他实现方式来帮助减少代码的大小。
-
那么miniKanren只是SICP和PAIP之类的" Lisp入门"吗? 我只是不明白所有的夸夸其谈。
暂定答案:
AFAIK,"理性的计划者"以Scheme-y语法和函数式编程风格介绍了基本逻辑编程,特别是将常量目标" #u"(失败)和" #s"(推导)添加到布尔值" #t"中"和" #f"。 它使用与Prolog相同的逻辑编程方法:统一和回溯搜索。 我将看看周末是否有时间从书架上取回那本书。 数学的分支是一种受限形式的一阶逻辑,在这种情况下为Horn子句,而其分辨率为Unfication。 请参阅:计算逻辑:过去的回忆
约翰·艾伦·罗宾逊(John Alan Robinson)撰写的《对未来的挑战》和《罗伯特·科瓦尔斯基(Robert Kowalski)的逻辑编程的早期》为冷门起步。
-
这两个引用与Kanren或MiniKanren有什么关系?
-
看到最后一个问题:"它们来自数学的哪些分支,其理论基础是什么?"