【问题标题】:Compound terms and Lists in PrologProlog 中的复合术语和列表
【发布时间】:2016-04-24 23:49:02
【问题描述】:

为什么在性能方面复合词比列表更受欢迎? 例如,

matrix(row(1,2,3),row(1,2,3),row(1,2,3))

优于

[[1,2,3],[1,2,3],[1,2,3],[1,2,3]]

【问题讨论】:

    标签: prolog


    【解决方案1】:

    首先,明确一点:列表一种复合词。

    要查看列表是什么,请使用write_canonical/1。例如,使用 GNU Prolog:

    | ?- write_canonical([1,2,3]).  
    '.'(1,'.'(2,'.'(3,[])))
    

    关于内存中的表示,我推荐 Richard O'Keefe 的书。系统之间的细节有所不同,但您可以确定要在内存中表示术语 row(1,2,3),您需要:

    • 一个用于函子/数量的存储单元
    • 3 个存储单元用于参数

    对于术语.(1, .(2, .(3, []) 在直接的内存表示中,您需要:

    • 三个内存单元用于三个 '.'/2 函子
    • 1、2、3 的 3 个存储单元
    • 可能更多(例如[])。

    由此,您已经看到,在这种表示形式中,使用列表至少会占用大约 两倍 的内存。

    您可以自己执行一些简单的测试,以帮助您了解这些表示在您的系统中的内存消耗差异。

    【讨论】:

    • 这很有趣。在计算方面,你知道这些表示是如何比较的吗?
    • 另外两个重要方面是(第一个)参数索引(请参阅您的 Prolog 系统或一本好书)和 arity 限制:使用列表,只有实体是原子[] 和术语'.'/2,因此(浅)第一个参数索引无法区分(例如)长度为1、2、3 等的列表。赞成列表:您的Prolog 系统可能对复合术语的arity 有固有的限制,因此在某些情况下,可能需要使用类似列表的结构来表示非常多元素的集合。 Richard 的书包含有关这两个方面的更有价值的信息以及更多信息。
    【解决方案2】:

    其他(优秀)答案没有提到的事情:

    通过位置访问列表成员意味着您需要遍历列表。应该可以在恒定时间内访问术语的参数。因此,对于随机访问,术语应该更有效。


    顺便说一句:您可以尝试使列表遍历速度稍微快一些。但是SWI-Prolog implementation of nth0_det/3 几乎是绝望的气味;)


    您可能对this thread 感兴趣,尤其是。 this summary 谈论列表和术语等。

    遵循一些经验法则。

    用例:

    • 如果您事先知道自己拥有多少东西,请使用一个术语。
    • 如果您可以拥有 0 个或多个相同的东西,请使用列表。
    • 如果您需要查找表,两者都不是最佳的

    效率:

    • 如果您想要随机访问,请使用术语
    • 如果您的算法在单链表上运行良好,那么 Prolog 列表是非常好的选择。

    从最后一点开始:尝试寻找使用链表而不是随机访问数组的算法。这并不总是可能的,但对于许多问题,您可以选择。经典的例子是快速排序与归并排序:在 Prolog 中,归并排序肯定更快。

    无论哪种方式,首先确保你做对了,然后再担心效率。并确保在开始优化之前衡量性能瓶颈。

    当然,选择最佳算法和数据结构意味着您需要了解您的问题和可用的工具。与您的问题无关,但曾经是 C++ 的“标准模板库”的美妙之处在于,对于算法和数据结构,时间复杂度(“大 O 表示法”)是固有属性,而不是实现细节。

    【讨论】:

    • 您确定“经典示例是快速排序与合并排序:在 Prolog 中,合并排序肯定更快。”?
    • @repeat 对于纯序言中的列表排序,我个人无法想出一个接近我自己的合并排序实现的快速排序实现。所以这句话说的更多是关于我的。
    • 哈哈,只是认真的:)但老实说……你已经彻底搜索过这些案例了吗?我还没有)。即便如此......与此同时,请从 Linus 的一句话中得到一些安慰:“理论和实践有时会发生冲突。当这种情况发生时,理论就会失败。每一次。”跨度>
    • @repeat 你实际上给了我一个想法:至少在 SWI-Prolog 中,=.. 相当快。我想知道将列表变成一个术语并使用nb_setarg 进行交换是否会使快速排序更快(这也可能避免相当多的垃圾创建/收集?)。不知道这算不算“作弊”。
    • 如何将其从 SO 评论级别移至 SO 问题级别?
    【解决方案3】:

    另一方面是访问特定元素时的时间性能。使用列表,您可以线性访问其元素。但是使用functor(arg1, arg2, ..., argn, ...) 形式的复合术语,我们可以使用标准的arg/3 内置谓词来不断访问任何参数。 IE。 O(N) 与 O(1) 与任何合理的 Prolog 实现。

    但是对于您提出的问题,没有明确的答案。最佳解决方案取决于您要解决的特定问题。例如,使用列表对所有参数应用操作可能会更快(与使用基于arg/3 的解决方案相比)。但这也取决于使用的 Prolog 系统。如果性能是主要关注点,那么基准测试是关键。只需避免在微观层面上进行操作,而是考虑如何在应用程序的所有处理它的部分中创建和访问术语。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-17
      • 1970-01-01
      • 1970-01-01
      • 2011-10-14
      • 2021-10-30
      • 2020-02-27
      • 1970-01-01
      相关资源
      最近更新 更多