【问题标题】:How do I prevent a Datalog rule from pruning nulls?如何防止 Datalog 规则修剪空值?
【发布时间】:2012-06-04 22:25:13
【问题描述】:

我有以下事实和规则:

% frequents(D,P) % D=drinker, P=pub
% serves(P,B) % B=beer
% likes(D,B)

frequents(janus, godthaab).
frequents(janus, goldenekrone).
frequents(yanai, goldenekrone).
frequents(dimi,  schlosskeller).

serves(godthaab, tuborg).
serves(godthaab, carlsberg).
serves(goldenekrone, pfungstaedter).
serves(schlosskeller, fix).

likes(janus, tuborg).
likes(janus, carlsberg).

count_good_beers_for_at(D,P,F) :- group_by((frequents(D,P), serves(P,B), likes(D,B)),[D,P],(F = count)).
possible_beers_served_for_at(D,P,B) :- lj(serves(P,B), frequents(D,R), P=R).

现在我想构建一个规则,当“饮酒者”“频率”大于 0 的每个酒吧中可用的“喜欢”啤酒的数量时,该规则应该像返回“真”的谓词一样工作。

当规则不返回元组时,我会认为谓词为真。如果谓词为假,我打算让它返回没有“喜欢”啤酒的酒吧。

如您所见,我已经有一个规则,可以计算给定酒吧中给定饮酒者的好啤酒。我还有一条规则,告诉我可供应啤酒的数量。

DES> count_good_beers_for_at(A,B,C)

{                                           
  count_good_beers_for_at(janus,godthaab,2)
}
Info: 1 tuple computed.          

如您所见,柜台不会返回经常光顾但有 0 个喜欢的啤酒的酒吧。我打算通过使用左外连接来解决这个问题。

DES> is_happy_at(D,P,Z) :- lj(serves(P,B), count_good_beers_for_at(D,Y,Z), (Y=P))

Info: Processing:
  is_happy_at(D,P,Z) :-
    lj(serves(P,B),count_good_beers_for_at(D,Y,Z),Y = P).
{                                           
  is_happy_at(janus,godthaab,2),
  is_happy_at(null,goldenekrone,null),
  is_happy_at(null,schlosskeller,null)
}
Info: 3 tuples computed.

这几乎是对的,除了它还给了我不常去的酒吧。我尝试添加一个额外的条件:

DES> is_happy_at(D,P,Z) :- lj(serves(P,B), count_good_beers_for_at(D,Y,Z), (Y=P)), frequents(D,P)

Info: Processing:
  is_happy_at(D,P,Z) :-
    lj(serves(P,B),count_good_beers_for_at(D,Y,Z),Y = P),
    frequents(D,P).
{                                           
  is_happy_at(janus,godthaab,2)
}
Info: 1 tuple computed.

现在我以某种方式过滤掉了所有包含空值的东西!我怀疑这是由于 DES 中的空值逻辑造成的。

我认识到我可能以错误的方式处理整个问题。任何帮助表示赞赏。

编辑:作业是“very_happy(D) ist wahr, genau dann wenn jede Bar, die Trinker D besucht, wenigstens ein Bier ausschenkt, das er mag。”转换为“very_happy(D) 为真,如果每个酒吧饮酒者 D 访问,至少提供 1 杯他喜欢的啤酒”。由于这个作业是关于 Datalog 的,我认为不使用 Prolog 绝对可以解决。

【问题讨论】:

  • 不知道,但我想如果有人关心数据记录标签,他们比关心元组的人更有可能提供帮助......
  • @andrewcooke:查看编辑历史。从昨天到今天我尝试了一批新标签时,我在这个问题上有了数据记录标签。遗憾的是,相关标签似乎都没有很多订阅者。
  • 啊,好的,对不起。好久没用了,实在忍不住……

标签: database prolog logic datalog


【解决方案1】:

我认为对于您的作业,您应该使用基本的 Datalog,而不是滥用聚合。问题的重点是如何表达普遍量化的条件。我搜索了“通用量化数据记录”,在第一个位置我发现deductnotes.pdf 断言:

一个全称量化的条件只能用存在量化和否定的等价条件来表示。

在该 PDF 中,您还会发现一个有用的示例(第 9 和 10 页)。

因此,我们必须重新表述我们的问题。我最终得到了这段代码:

not_happy(D) :-
  frequents(D, P),
  likes(D, B),
  not(serves(P, B)).

very_happy(D) :-
  likes(D, _),
  not(not_happy(D)).

这似乎是需要的:

DES> very_happy(D)

{                                           
}
Info: 0 tuple computed.          

请注意likes(D, _),这是为了避免 yanai 和 dimi 被列为very_happy,而没有明确断言他们喜欢什么(OT 对不起,我的英语真的很烂...)

编辑:很抱歉,上述解决方案不起作用。我是这样改写的:

likes_pub(D, P) :-
  likes(D, B),
  serves(P, B).

unhappy(D) :-
  frequents(D, P),
  not(likes_pub(D, P)).

very_happy(D) :-
  likes(D, _),
  not(unhappy(D)).

测试:

DES> unhappy(D)

{                                           
  unhappy(dimi),
  unhappy(janus),
  unhappy(yanai)
}
Info: 3 tuples computed.          

DES> very_happy(D)

{                                           
}
Info: 0 tuples computed.          

现在我们添加一个事实:

serves(goldenekrone, tuborg).

我们可以看到更正后的代码结果:

DES> unhappy(D)

{                                           
  unhappy(dimi),
  unhappy(yanai)
}
Info: 2 tuples computed.          

DES> very_happy(D)

{                                           
  very_happy(janus)
}
Info: 1 tuple computed.          

【讨论】:

  • OP 要求在每家酒吧提供“可用的”喜欢的“啤酒,“饮酒者”“频率”大于 0。大于 0 仅表示存在,universal 表示计数等于所有啤酒或类似的。我不认为 OP 要求通用。
  • 从我的回答中应该清楚,聚合与所提出的问题无关。我认为is_happy_at/2 只是一个简单的连词。即使我们有计数器,我们仍然必须解决each引入的全称量化
  • 是的。 forall 中的双重否定与普遍量化所需的 2 个否定具有相似的作用。在 Datalog 中,我们没有其他工具...
  • 好的,我现在明白你的意思了。同意这个问题涉及“forall”。这里的问题是范围限制,你从哪里获取个体集?您的 like/2 投影是合法的。
  • 前两个 cmets 被交换。按时间顺序是 Cookie Monster 6 月 8 日 18:47 然后 chac 18:46 (不同地区的时间不准确?)
【解决方案2】:

也许不是您期望的答案。但是您可以使用普通的 Prolog 并使用 bagof/3 或 setof/3 内置谓词轻松地进行分组查询。

?- bagof(B,(frequents(D,P), serves(P,B), likes(D,B)),L), length(L,N).
D = janus,
P = godthaab,
L = [tuborg,carlsberg],
N = 2

bagof/3 的语义是它不会为给定的查询计算外连接。查询通常由 Prolog 执行。结果首先被累加和键排序。最后通过回溯返回结果。如果您的数据记录不能没有空值,那么是的,您必须进行过滤。

但是,当您只想知道喜欢的啤酒的存在时,您不需要进行聚合。您可以通过查询直接完成,无需任何聚合:

is_happy_at(D,P) :- frequents(D,P), once((serves(P,B), likes(D,B))).
?- is_happy_at(D,P).
D = janus,
P = godthaab ;
Nein

once/1 可防止不必要的回溯。当 Datalog 在 is_happy_at/2 中看到投影时,Datalog 可能会自动不进行不必要的回溯,即 B 被投影掉。或者您可能需要显式使用与 SQL DISTINCT 对应的内容。或者最终,您的数据日志会为您提供与 SQL EXISTS 最接近的对应于 once/1 的内容。

再见

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多