0

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

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

  • 标记整数的优势(即使用pos(X)而不是XifX是一个正整数来利用模式匹配)
  • 使谓词尽可能具有确定性的内在魔力(不要在序列末尾留下选择点)
  • 将“标志”数组传递给谓词以微调行为的兴趣(在这种情况下,如果序列为空,它应该抛出还是失败?)

但是也:

  • ISO 标准异常术语的未经过深思熟虑的格式(使用复合术语而不是列表来传递信息...... WTF!)
  • 抛出异常的谓词的命名library(error)(为什么不调用它们throw_...而不是混淆地给它们与异常术语相同的名称,人们真的想要call(domain_error(...))吗?
  • must_be/2无法获取有关哪个 arg 确切导致问题的附加位置信息的事实(为什么!)

完整的代码是between_with_step.pl ...尚未完全单元测试。

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

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

它发出整数递增或递减序列的下一个值。它使用标记值的模式匹配。特别是,“如果序列是整数的结束值”(与表示无穷大的原子相反)和“步长为正”的情况由以下与术语匹配的子句int(End)和子句给出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)- 这是该系列中标记为的最后一个

  • int/1在第二个位置和
  • pos/1排在第三位?

这个 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(_)

4

2 回答 2

2

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

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

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

?- second(2,tag1(1)).
true ;
false.
于 2020-05-17T13:05:11.387 回答
1

这取决于 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 特定的索引。

于 2021-01-25T00:49:36.803 回答