选择点修剪需要削减,但我认为编译器应该足够敏锐,可以自行完成

Choicepoint pruning needs a cut, but I think the compiler should be sharp enough to do it by itself

我正在做一个练习,写一个需要额外步长值的 between/3

这是一个有趣的练习,快速显示:

还有:

完整的代码是 between_with_step.pl ... 还没有完全单元测试。

现在我已经设置了以下谓词

between_enum(+Start,+TaggedEnd,+TaggedStep,-Value)

发出递增或递减整数序列的下一个值。它使用标记值的模式匹配。特别是,"end value if the sequence is an integer"(与表示 无穷大 的原子相反)和 "the step is positive" 的情况由以下与术语 [=26= 匹配的子句子集给出] 和 pos(Step):

% ---
% Case of "positive step" pos(Step) and "integer end" int(End) (not infinite end)
% ---

% Past end of sequence. Occurs only if the sequence is empty on entry.

between_enum(Start,int(End),pos(_),_) :- 
   Start > End,!,fail. 

% Last entry in sequence, emit "Start" as "Value" and don't allow backtracking!
% The test "Start < End" is redundant here.

between_enum(Start,int(End),pos(Step),Start) :- 
   Start < End, Start+Step >  End, !. 

% More entries exist in sequence, emit "Start" as "Value" and DO allow backtracking!
% The test "Start < End" is redundant here.
% The test "Start+Step =< End" is redundant here, being the complement of the cut-off preceding clause

between_enum(Start,int(End),pos(Step),Start) :-
   Start < End, Start+Step =< End.    

% Recursive alternative to the above, digging for more values!
% The test "Start < End" is redundant here.
% The test "Start+Step =< End" is redundant here

between_enum(Start,int(End),pos(Step),Value) :-
   Start < End, Start+Step =< End, 
   NewStart is Start+Step, 
   %!, % NEEDED TO MAINTAIN DETERMINACY ON LAST VALUE
   between_enum(NewStart,int(End),pos(Step),Value).

现在,为了在枚举结束时完全确定,以下子句需要删减:

between_enum(Start,int(End),pos(Step),Value) :-
   Start < End, Start+Step =< End, 
   NewStart is Start+Step, 
   !, % <---- HERE
   between_enum(NewStart,int(End),pos(Step),Value). 

否则:

有剪裁:

?- between(10,15,1,Value).
Value = 10 ;
Value = 11 ;
Value = 12 ;
Value = 13 ;
Value = 14 ;
Value = 15.        % This is the end for sure!

没有剪切:

?- between(10,15,1,Value).
Value = 10 ;
Value = 11 ;
Value = 12 ;
Value = 13 ;
Value = 14 ;
Value = 15 ;      % Unsure whether this is the end?
false.            % Well, turns out it is the end, really!

编译器不应该足够强大来确定在 between_enum(Start,int(End),pos(Step),Value) 之后没有进一步的匹配是可能的 - 这是标记为

的系列中的最后一个

这个 SWI-Prolog 8.1.

编辑

可能是编译器只索引了前两个参数。不需要切入

between_enum(Start,int(End),neg(Step),Value)

后面只有

between_enum(Start,inf,neg(Step),Value)

以及

between_enum(Start,minf,neg(Step),Value)

所以它可以很好地区分 infminfint(_)

你对参数顺序的直觉是正确的,可以通过一个简单的实验来证实。

first(tag1(_),_).
first(tag2(_),_).

second(_,tag1(_)).
second(_,tag2(_)).
?- first(tag1(1),2).
true.

?- second(2,tag1(1)).
true ;
false.

这是 Prolog 系统相关的,并且取决于可用的自动索引或可用的索引指令。例如 SWI-Prolog 有自动深度索引,但有一些关于自动多参数索引的特性。所以对于来自 madgen 的例子:

first(tag1(_),_).
first(tag2(_),_).

second(_,tag1(_)).
second(_,tag2(_)).

我进入我的系统,两个查询都不留选择点:

Jekejeke Prolog 4, Runtime Library 1.4.7

?- first(tag1(1),2).
Yes

?- second(2,tag1(1)).
Yes

另一方面,在SWI-Prolog中,第二个查询中留下了一个选择点:

SWI-Prolog (threaded, 64 bits, version 8.3.17)

?- first(tag1(1),2).
true.

?- second(2,tag1(1)).
true ;
false.

这可能会很烦人,而且经常需要使用外观谓词 用于重新排序参数,使它们更适合 SWI-Prolog 特定索引。