我正在尝试复制标准长度/ 2谓词的行为。特别是,我希望我的谓词适用于有界和无界的参数,如下例所示:
% Case 1
?- length(X, Y).
X = [],
Y = 0 ;
X = [_G4326],
Y = 1 ;
X = [_G4326, _G4329],
Y = 2 ;
X = [_G4326, _G4329, _G4332],
Y = 3 .
% Case 2
?- length([a,b,c], X).
X = 3.
% Case 3
?- length(X, 4).
X = [_G4314, _G4317, _G4320, _G4323].
% Case 4
?- length([a,b,c,d,e], 5).
true.
简单实施:
my_length([], 0).
my_length([_|T], N) :- my_length(T, X), N is 1+X.
有一些问题。在案例3中,在产生正确答案之后,它进入无限循环。 是否可以将此谓词转换为确定性谓词?或者使用false停止的非确定性谓词?
是!但是使用红色切割。请参阅:https://stackoverflow.com/a/15123016/1545971
过了一段时间,我设法编写了一组谓词,模仿了内置长度/ 2的行为。 my_len_tail是确定性的,并且在所有情况1-4中都是正确的。 可以更简单吗?
my_len_tail(List, Len) :- var(Len)->my_len_tailv(List, 0, Len);
my_len_tailnv(List, 0, Len).
my_len_tailv([], Acc, Acc).
my_len_tailv([_|T], Acc, Len) :-
M is Acc+1,
my_len_tailv(T, M, Len).
my_len_tailnv([], Acc, Acc) :- !. % green!
my_len_tailnv([_|T], Acc, Len) :-
Acc<Len,
M is Acc+1,
my_len_tailnv(T, M, Len).
正如@DanielLyons在评论中建议的那样,可以使用clpfd推迟小于检查。但它仍然存在一个问题:在案例3(my_len_clp(X, 3)
)中,谓词是不确定的。 如何修复?
:-use_module(library(clpfd)).
my_len_clp(List, Len) :- my_len_clp(List, 0, Len).
my_len_clp([], Acc, Acc).
my_len_clp([_|T], Acc, Len) :-
Acc#<Len,
M is Acc+1,
my_len_clp(T, M, Len).
可以使用CLP(FD)库中的zcompare/3
修复。请参阅:https://stackoverflow.com/a/15123146/1545971
答案 0 :(得分:5)
在SWI-Prolog中,非确定性问题可以通过CLP(FD)的zcompare/3
来解决,它将不平等性化为可用于索引的术语:
:- use_module(library(clpfd)).
my_length(Ls, L) :-
zcompare(C, 0, L),
my_length(Ls, C, 0, L).
my_length([], =, L, L).
my_length([_|Ls], <, L0, L) :-
L1 #= L0 + 1,
zcompare(C, L1, L),
my_length(Ls, C, L1, L).
您的示例现在是确定性的(因为最近版本的SWI-Prolog执行即时索引):
?- my_length(Ls, 3).
Ls = [_G356, _G420, _G484].
所有严肃的Prolog实现都附带了CLP(FD),在这里使用它非常有意义。要求您的供应商实施zcompare/3
或更好的替代方案(如果尚未提供)。
答案 1 :(得分:3)
有关一组测试用例,请参阅this table和current definition in the prologue。还有许多奇怪的案例需要考虑。
使用length/2
,var/nonvar
等定义is/2
并非完全无关紧要,因为(is)/2
和算术比较非常有限。也就是说,它们非常频繁地产生instantiation_error
s而不是相应地成功。只是为了说明这一点:使用successor-arithmetics定义length_sx/2
是微不足道的。
length_sx([], 0).
length_sx([_E|Es], s(X)) :-
length_sx(Es, X).
这个定义非常完美。 length_sx(L, L)
甚至失败了。唉,继承算术是不能有效支持的。也就是说,整数 i 需要O( i )空间而不是O(log i ),这是人们所期望的。
我更喜欢的定义是:
length_fd([],0).
length_fd([_E|Es], L0) :-
L0 #> 0,
L1 #= L0-1,
length_fd(Es, L1).
哪个是最直接的翻译。具有已知长度的效率非常高,但是否则后面的约束开销显示出来。此外,还存在这种不对称性:
?- length_fd(L,0+0).
false.
?- length_fd(L,0+1).
L = [_G919] ;
false.
但是,使用library(clpfd)
的定义特别优雅和,即使对于更精细的案例也是如此。它没有内置长度快......
?- time(( length_fd(L,N),N=1000 )).
% 29,171,112 inferences, 4.110 CPU in 4.118 seconds (100% CPU, 7097691 Lips)
L = [_G67, _G98, _G123, _G159, _G195, _G231, _G267, _G303, _G339|...],
N = 1000 .
?- time(( my_len_clp(L,N),N=10000 )).
% 1,289,977 inferences, 0.288 CPU in 0.288 seconds (100% CPU, 4484310 Lips)
L = [_G67, _G79, _G82, _G85, _G88, _G91, _G94, _G97, _G100|...],
N = 10000 .
?- time(( length(L,N),N=10000 )).
% 30,003 inferences, 0.006 CPU in 0.006 seconds (100% CPU, 4685643 Lips)
L = [_G67, _G70, _G73, _G76, _G79, _G82, _G85, _G88, _G91|...],
N = 10000 .
...但是它能够正确处理约束:
?- N in 1..2, my_len_clp(L,N).
N = 1,
L = [_G1439] ;
N = 2,
L = [_G1439, _G1494] ;
false.
?- N in 1..2, length(L,N).
N = 1,
L = [_G1445] ;
N = 2,
L = [_G1445, _G1448] ;
*LOOPS*
答案 2 :(得分:1)
我对这个答案并不是特别自信,但我的想法是否定的,你必须做一些额外的工作才能让Prolog为length/2
做正确的事,这是一个真正的耻辱,因为它是如此伟大的“教程“在最简单的演示文稿中断言。
我提交了证据the source code to this function in SWI-Prolog和the source in GNU Prolog。这些都不是一个简洁,可爱的技巧,它看起来像他们都通过测试参数工作,然后根据实例化的参数将处理延迟到不同的内部函数。
我很想知道这个错误。我常常想知道为什么它是如此容易编写member/2
这样做是正确的,但很难编写length/2
。 Prolog在算术上并不擅长,但它真的那么糟糕吗?这里希望别人能有更好的答案。
答案 3 :(得分:-1)
这适用于所有测试用例(但它有红色切割):
my_length([], 0).
my_length([_|T], N) :-
( integer(N) ->
!,
N > 0,
my_length(T, X), N is 1 + X, !
;
my_length(T, X), N is 1 + X
).
答案 4 :(得分:-1)
(我试图编辑@ false的response,但它被拒绝了)
my_len_tail/2
在生成列表时比buldin length/2
更快(就推理数量和实际时间而言),但是N in 1..2
约束存在问题。
?- time(( my_len_tail(L,N),N=10000000 )).
% 20,000,002 inferences, 2.839 CPU in 3.093 seconds (92% CPU, 7044193 Lips)
L = [_G67, _G70, _G73, _G76, _G79, _G82, _G85, _G88, _G91|...],
N = 10000000 .
?- time(( length(L,N),N=10000000 )).
% 30,000,004 inferences, 3.557 CPU in 3.809 seconds (93% CPU, 8434495 Lips)
L = [_G67, _G70, _G73, _G76, _G79, _G82, _G85, _G88, _G91|...],
N = 10000000 .
答案 5 :(得分:-1)
goal_expansion((_lhs_ =:= _rhs_),(when(ground(_rhs_),(_lhs_ is _rhs_)))) .
:- op(2'1,'yfx','list') .
_list_ list [size:_size_] :-
_list_ list [size:_size_,shrink:_shrink_] ,
_list_ list [size:_size_,shrink:_shrink_,size:_SIZE_] .
_list_ list [size:0,shrink:false] .
_list_ list [size:_size_,shrink:true] :-
when(ground(_size_),(_size_ > 0)) .
[] list [size:0,shrink:false,size:0] .
[_car_|_cdr_] list [size:_size_,shrink:true,size:_SIZE_] :-
(_SIZE_ =:= _size_ - 1) ,
(_size_ =:= _SIZE_ + 1) ,
_cdr_ list [size:_SIZE_] .
/*
?- L list Z .
L = [],
Z = [size:0] ? ;
L = [_A],
Z = [size:1] ? ;
L = [_A,_B],
Z = [size:2] ? ;
L = [_A,_B,_C],
Z = [size:3] ?
yes
?- L list [size:0] .
L = [] ? ;
no
?- L list [size:1] .
L = [_A] ? ;
no
?- L list [size:2] .
L = [_A,_B] ? ;
no
?- [] list [size:S] .
S = 0 ? ;
no
?- [a] list [size:S] .
S = 1 ? ;
no
?- [a,b] list [size:S] .
S = 2 ? ;
no
?- [a,b,c] list [size:S] .
S = 3 ? ;
no
?-
*/