我想复制标准的length/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.
这段代码存在问题。在第三种情况下,程序会输出正确答案后进入无限循环。这个谓词能否被转化为一个确定性的谓词?或者是一个不确定性的谓词,但返回false时停止运行?
可以!但需要使用red cut。详见:https://dev59.com/xGUp5IYBdhLWcg3wrY9p#15123016
经过一段时间的努力,我已经编写出了一组谓词来模仿内置的length/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来推迟小于检查。但是仍然存在一个问题:在Case 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://dev59.com/xGUp5IYBdhLWcg3wrY9p#15123146
my_len_clp/3
,它非常高效! - false