第一个参数索引
First argument indexing
我想知道如何在各种 Prolog 实现上实现智能第一个参数索引。
特别是,像 integer/1
紧跟在子句 "neck" 之后的简单类型测试目标可以 有助于更好的索引。
考虑:
foo(h(X),X).
foo([],nil).
foo([_|_],cons).
foo(X,Y) :- integer(X), Y = n(X).
通过此子句排序,我希望目标 foo([],_)
成功 而不会 留下任何无用的选择点。
不幸的是,SWI Prolog 没有搞清楚:
?- length(Xs,10),
maplist(=([]),Xs),
statistics(trailused,T1),
maplist(foo,Xs,Ys),
statistics(trailused,T2).
T1 = 5792,
T2 = 5968,
Xs = [[], [], [], [], [], [], [], [], [], []],
Ys = [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil] ...
其他 Prolog 实现是否做得更好?
据我所知,Prolog 中的子句索引仅基于谓词参数(通常仅第一个)的语法,因此可以在编译时完成。在您的示例中,将最后一个子句移动到顶部并在 integer(X)
之后插入一个剪切,至少在某些实现中,会导致其他子句被索引,并且最初会为调用此创建一个选择点谓词。查看正文中的第一个目标会减慢索引过程,通常对执行时间的好处太小。
是的,ECLiPSe 系统会这样做。
正如您所建议的,它考虑了一些简单的内置谓词(例如 integer/1, =/2, !/0
)以用于索引目的。然后,您的示例在没有选择点的情况下确定地执行 foo/2
的所有调用,并实例化第一个参数。此外,ECLiPSe 会在任何参数上执行此操作,而不仅仅是第一个。
您可以在论文中找到更多细节 ECLiPSe - from LP to CLP。
回答您的后续问题:不需要额外的 VM 功能,生成的 VM 代码如下所示:
foo / 2:
switch_on_type a(1)
list: ref(L5)
structure: ref(L1)
bignum: ref(L7)
[]: ref(L4)
integer: ref(L7)
meta: ref(L0)
label(L0):
try 0 2 ref(L1)
retry 0 ref(L3)
trust 0 ref(L5)
label(L1):
get_structure a(1) h / 1 ref(L2)
write_value a(2)
ret
label(L2):
read_value a(2)
ret
label(L3):
get_nil a(1)
label(L4):
get_atom a(2) nil
ret
label(L5):
get_list a(1) ref(L6)
write_void 2
label(L6):
get_atom a(2) cons
ret
label(L7):
get_structure a(2) n / 1 ref(L8)
write_value a(1)
ret
label(L8):
read_value a(1)
ret
YAP 是另一个提供谓词子句扩展索引的 Prolog 系统:
$ yap
YAP 6.3.4 (x86_64-darwin14.3.0): Wed Apr 22 22:26:34 WEST 2015
?- [user].
% consulting user_input...
foo(h(X),X).
| foo([],nil).
| foo([_|_],cons).
| foo(X,Y) :- integer(X), Y = n(X).
| % consulted user_input in module user, 1 msec 0 bytes
true.
?- foo([],_).
true.
一些关于 YAP 索引功能的相关论文是:
我们最近向 Jekejeke Prolog 添加了保护索引。以下保护索引已经存在一段时间了:
p(..., X, ...) :- ..., var(X), ...
我们现在将这种处理扩展到更多的警卫。这将在即将发布的 1.3.4 版本中提供:
p(..., X, ...) :- ..., X = f(...), ...
p(..., X, ...) :- ..., functor(X, f, ...), ...
这些新的守卫工作得很好,因为它们会生成额外的子句键,这些键已经是现有索引的一部分。
但目前我们现有的索引不允许查找 Prolog 类型,只能查找原子值,因此我们目前无法实现整数 (X) 保护,即使检测本身不会很困难。
实现很简单,一些指令我们不需要查。相反,我们可以直接搜索更简单的 Jekejeke Prolog 中间格式的正文:
开源:Java class 索引,方法 getGuard()
https://github.com/jburse/jekejeke-devel/blob/master/jekrun/headless/jekpro/model/rope/Index.java#L105
我想知道如何在各种 Prolog 实现上实现智能第一个参数索引。
特别是,像 integer/1
紧跟在子句 "neck" 之后的简单类型测试目标可以 有助于更好的索引。
考虑:
foo(h(X),X).
foo([],nil).
foo([_|_],cons).
foo(X,Y) :- integer(X), Y = n(X).
通过此子句排序,我希望目标 foo([],_)
成功 而不会 留下任何无用的选择点。
不幸的是,SWI Prolog 没有搞清楚:
?- length(Xs,10),
maplist(=([]),Xs),
statistics(trailused,T1),
maplist(foo,Xs,Ys),
statistics(trailused,T2).
T1 = 5792,
T2 = 5968,
Xs = [[], [], [], [], [], [], [], [], [], []],
Ys = [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil] ...
其他 Prolog 实现是否做得更好?
据我所知,Prolog 中的子句索引仅基于谓词参数(通常仅第一个)的语法,因此可以在编译时完成。在您的示例中,将最后一个子句移动到顶部并在 integer(X)
之后插入一个剪切,至少在某些实现中,会导致其他子句被索引,并且最初会为调用此创建一个选择点谓词。查看正文中的第一个目标会减慢索引过程,通常对执行时间的好处太小。
是的,ECLiPSe 系统会这样做。
正如您所建议的,它考虑了一些简单的内置谓词(例如 integer/1, =/2, !/0
)以用于索引目的。然后,您的示例在没有选择点的情况下确定地执行 foo/2
的所有调用,并实例化第一个参数。此外,ECLiPSe 会在任何参数上执行此操作,而不仅仅是第一个。
您可以在论文中找到更多细节 ECLiPSe - from LP to CLP。
回答您的后续问题:不需要额外的 VM 功能,生成的 VM 代码如下所示:
foo / 2:
switch_on_type a(1)
list: ref(L5)
structure: ref(L1)
bignum: ref(L7)
[]: ref(L4)
integer: ref(L7)
meta: ref(L0)
label(L0):
try 0 2 ref(L1)
retry 0 ref(L3)
trust 0 ref(L5)
label(L1):
get_structure a(1) h / 1 ref(L2)
write_value a(2)
ret
label(L2):
read_value a(2)
ret
label(L3):
get_nil a(1)
label(L4):
get_atom a(2) nil
ret
label(L5):
get_list a(1) ref(L6)
write_void 2
label(L6):
get_atom a(2) cons
ret
label(L7):
get_structure a(2) n / 1 ref(L8)
write_value a(1)
ret
label(L8):
read_value a(1)
ret
YAP 是另一个提供谓词子句扩展索引的 Prolog 系统:
$ yap
YAP 6.3.4 (x86_64-darwin14.3.0): Wed Apr 22 22:26:34 WEST 2015
?- [user].
% consulting user_input...
foo(h(X),X).
| foo([],nil).
| foo([_|_],cons).
| foo(X,Y) :- integer(X), Y = n(X).
| % consulted user_input in module user, 1 msec 0 bytes
true.
?- foo([],_).
true.
一些关于 YAP 索引功能的相关论文是:
我们最近向 Jekejeke Prolog 添加了保护索引。以下保护索引已经存在一段时间了:
p(..., X, ...) :- ..., var(X), ...
我们现在将这种处理扩展到更多的警卫。这将在即将发布的 1.3.4 版本中提供:
p(..., X, ...) :- ..., X = f(...), ...
p(..., X, ...) :- ..., functor(X, f, ...), ...
这些新的守卫工作得很好,因为它们会生成额外的子句键,这些键已经是现有索引的一部分。
但目前我们现有的索引不允许查找 Prolog 类型,只能查找原子值,因此我们目前无法实现整数 (X) 保护,即使检测本身不会很困难。
实现很简单,一些指令我们不需要查。相反,我们可以直接搜索更简单的 Jekejeke Prolog 中间格式的正文:
开源:Java class 索引,方法 getGuard()
https://github.com/jburse/jekejeke-devel/blob/master/jekrun/headless/jekpro/model/rope/Index.java#L105