部分字典/记录统一?
Posted
技术标签:
【中文标题】部分字典/记录统一?【英文标题】:Partial Dictionary/Record Unification? 【发布时间】:2012-09-30 07:38:10 【问题描述】:我了解一些 Prolog 支持开箱即用的类字典关联数据结构。对于这样做的实现,它们是否支持与另一个实际上不包含所有键的结构部分统一的概念?
例如在core.logic/miniKanren的语法中:
(run* [q]
(== :foo 1 :bar 2 (partial-map :foo q)))
这将返回 q 绑定为 1 的单个结果。
Prologs 是否给这个操作或这个部分结构命名?
【问题讨论】:
我进行了基本搜索,但没有找到任何 Prolog 提供的哈希表。可能是因为 Prolog 规则库通常是通过精心设计的散列表实现的,因此散列是“内在的”并且更多的是“实现细节”而不是广泛需要的功能。例如,参见 SWI-Prolog 作者的 this 帖子。 不知道这是否仍然让您感兴趣,但recently I posted here 是可扩展记录的一个非常简单的实现,我可以调用?- attr(A, [foo-1, bar-2]), attr(A,[foo-Q]).
并返回Q=1
。 impl'n:attr(A, [N-V|R]):- memberchk( N-X, A), X=V, attr(A, R).
; attr(_, []).
仅此而已。
【参考方案1】:
一般来说,解决 Prolog 中基本数据类型选择不佳的标准方法是:通过添加库和使用接口。例如,SWI-Prolog 带有实现基于 AVL 树的关联数据结构的 assoc
library。 (顺便说一句,平衡树在功能和逻辑编程中比哈希表更常见,因为在树上创建“持久”数据结构比哈希表更容易——在共享内部结构的 FP 意义上持久。)
使用这个库看起来像这样:
?- [library(assoc)].
% library(assoc) compiled into assoc 0.00 sec, 97 clauses
true.
?- empty_assoc(Assoc).
Assoc = t.
?- empty_assoc(Assoc), get_assoc(test, Assoc, V).
false.
?- empty_assoc(Assoc), put_assoc(test, Assoc, foo, Assoc2).
Assoc = t,
Assoc2 = t(test, foo, -, t, t).
?- empty_assoc(Assoc),
put_assoc(test, Assoc, foo, Assoc2),
get_assoc(test, Assoc2, Value).
Assoc = t,
Assoc2 = t(test, foo, -, t, t),
Value = foo.
一旦你有了这样的接口,你就可以在它上面定义各种逻辑关系。一旦你有了逻辑关系,Prolog 的正常统一机制就会处理剩下的事情——不需要对这种或那种数据类型的特殊支持。根据您的要求,我认为您想要的就像一个子集关系,除了检查一个关联的所有都在另一个关联中并且它们都具有相同的值。我想这看起来像这样:
association_subset(Left, Right) :-
forall(gen_assoc(Assoc, Left, Value), get_assoc(Assoc, Right, Value)).
只有当左关联是右关联的子集时,此谓词才为真,如上所定义。我们可以对其进行测试,看看它是否在做我们想要的:
simple(Assoc) :-
empty_assoc(Empty),
put_assoc(foo, Empty, foo_test, V1),
put_assoc(bar, V1, bar_test, Assoc).
complex(Assoc) :-
simple(Assoc1),
put_assoc(baz, Assoc1, bazzle, Assoc).
unrelated(Assoc) :-
empty_assoc(Empty),
put_assoc(baz, Empty, bazzle, Assoc).
...
?- simple(X), complex(Y), association_subset(X, Y).
X = t(foo, foo_test, <, t(bar, bar_test, -, t, t), t),
Y = t(baz, bazzle, -, t(bar, bar_test, -, t, t), t(foo, foo_test, -, t, t)).
?- simple(X), simple(Y), association_subset(X, Y).
X = Y, Y = t(foo, foo_test, <, t(bar, bar_test, -, t, t), t).
?- simple(X), unrelated(Y), association_subset(X, Y).
false.
?- complex(X), simple(Y), association_subset(X, Y).
false.
我们可以将其翻译为您的确切问题,如下所示:
left(Assoc) :-
empty_assoc(Empty),
put_assoc(foo, Empty, 1, Assoc).
right(Assoc) :-
left(Assoc1),
put_assoc(bar, Assoc1, 2, Assoc).
?- left(L), right(R), association_subset(L, R), get_assoc(foo, L, Q).
L = t(foo, 1, -, t, t),
R = t(foo, 1, <, t(bar, 2, -, t, t), t),
Q = 1.
我意识到这个答案并不能真正回答您提出的问题,但我希望它回答了问题下方的问题。换句话说,对于这些数据结构不需要需要特别支持——上面的谓词也可以定义在关联列表上,你可以看到你所需要的只是通常的方法建立空关联、添加、测试和生成关联的键/值以及底层数据结构变得无关紧要。无论是数据结构还是统一,都不需要特殊支持。特殊的语法肯定会让它看起来更好看!但是没有必要得到你想要的行为。
【讨论】:
感谢您的解释!我知道我可以从头开始实现它,但我很好奇这种模式是否足够普遍,Prologs 可以对其提供特定的支持,以及这种支持是否有名称。【参考方案2】:某些 Prolog 系统(例如 Eclipse)具有记录符号。这个可以用 当您提前知道地图的可能键时。但它需要 类型声明。记录符号也可以在 Prolog 后代中找到 Erlang 等语言。
这个想法很简单。你先声明 记录类型(这里发明了一些语法):
:- rectype TK1,...,Kn.
现在您可以在 Prolog 程序中使用 记录,只要写(这里又发明了一些语法):
... TF1 = V1, .., Fn = Vm ...
在编译类型时,记录将被转换为复合 然后可以很容易地用于正常的统一。转换 根据记录类型重新排序键值对 声明,然后删除键并仅使用位置。 未使用的位置被匿名变量替换或 如果记录类型声明也是默认值 涵盖了这个。
... T(W1, ..., Wn) ...
您的示例将按如下方式工作:
:- rectype myrecfoo, bar
?- myrecfoo=1,bar=2 = myrecfoo=q
后一个查询将在内部执行为:
?- myrec(1,2) = myrec(q,_).
有关 Eclipse 的详细信息,请参见此处的示例:http://www.eclipseclp.org/doc/bips/kernel/syntax/struct-1.html
对于键集不是静态的动态地图,您可以实现 动态数据结构作为关于 SWI-Prolog AVL 的另一篇文章 树显示。或者向您的 Prolog 系统询问特定的句柄 数据结构。使用 FFI(外部函数接口)实现这些 或访问已与 Prolog 系统捆绑的这些。 例如 Eclipse 捆绑了一对,请参阅“描述”部分 在下面的文章中:http://www.eclipseclp.org/doc/bips/kernel/record/index.html
再见
【讨论】:
谢谢!我一直在寻找像这样的支持的具体示例。【参考方案3】:我并不清楚你真正想要什么(你已经删除了散列方面),但也许你更想要特征术语或特征结构?
它们很受语言学家的欢迎,并且已经成为生活的一部分。
可以在属性变量的帮助下实现它们,但到目前为止,我还没有看到对它们的太多需求。
您还可以通过句法统一来稍微笨拙地模拟它们。它很笨拙,因为您需要用一个单独的参数来表示每个特征(您可以做得稍微好一点,但它也更复杂)。因此,如果您的程序包含 n 个特征,则一个特征结构将包含 n 个不同的参数,其中大多数永远不会被触及。但是,统一将直接按预期工作。
【讨论】:
是的,散列方面并不重要,只是一个关联数据结构,其中键顺序对用户无关紧要。是的,必须显式枚举每个参数会不太理想 - 上面提到的 Eclipse 糖似乎可以按照您的描述进行操作,而无需强制用户手动执行此操作。以上是关于部分字典/记录统一?的主要内容,如果未能解决你的问题,请参考以下文章