我正在编写一个between/3
需要额外步长值的练习。
这是一个有趣的练习,快速显示:
- 标记整数的优势(即使用
pos(X)
而不是X
ifX
是一个正整数来利用模式匹配) - 使谓词尽可能具有确定性的内在魔力(不要在序列末尾留下选择点)
- 将“标志”数组传递给谓词以微调行为的兴趣(在这种情况下,如果序列为空,它应该抛出还是失败?)
但是也:
- 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)
所以它可以很好地区分inf
,minf
和int(_)
。