Я делаю упражнение по написанию between/3
, которое принимает дополнительное значение шага.
Это интересное упражнение, быстро показывающее:
- преимущество тегированных целых чисел (т.е. используйте
pos(X)
вместоX
, еслиX
является положительным целым числом, чтобы воспользоваться преимуществом сопоставления с образцом) - магия, заключающаяся в том, чтобы сделать предикат максимально детерминированным (не оставляйте открытой точку выбора в конце последовательности)
- заинтересованность в передаче массива «флагов» в предикат для точной настройки поведения (в этом случае должен ли он выдать или дать сбой, если последовательность пуста?)
Но также:
- менее чем хорошо продуманный формат стандартных условий исключения ISO (использование составных терминов вместо списков для передачи информации ... WTF!)
- именование предикатов исключения в
library(error)
(почему бы не называть ихthrow_...
вместо того, чтобы путать их с тем же именем, что и термин исключения, действительно ли люди захотятcall(domain_error(...))
? - тот факт, что
must_be/2
не может получить дополнительную информацию о позиции о том, какой именно аргумент вызвал проблему (почему!)
Полный код - 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
на 2 позиции и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(_)
.
jiti_list/1
, который якобы сообщает вам о способе индексации предиката. В документации не говорится, насколько нова эта функция, ее нет в моей древней версии 7.2.3. - person Isabelle Newbie   schedule 17.05.2020error/2
. Первый аргумент - это термины в формеinstantiation_error/0
,syntax_error/1
илиdomain_error/2
. Второй аргумент доступен для передачи дополнительной информации в любом стиле, который вы выберете. В общем, здесь больше значит меньше. - person repeat   schedule 19.05.2020throw/1
напрямую. В целом, простота - это хорошо. Делай одно дело и делай это хорошо ... в стиле UNiX! - person repeat   schedule 20.05.2020