Обрезка точки выбора требует сокращения, но я думаю, что компилятор должен быть достаточно резким, чтобы сделать это самостоятельно.

Я делаю упражнение по написанию 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(_).


person David Tonhofer    schedule 17.05.2020    source источник
comment
Индексирование SWI описано на странице swi-prolog.org/pldoc/man?section=jitindex.. Он утверждает, что выполняет индексацию по нескольким аргументам, и упоминает предикат jiti_list/1, который якобы сообщает вам о способе индексации предиката. В документации не говорится, насколько нова эта функция, ее нет в моей древней версии 7.2.3.   -  person Isabelle Newbie    schedule 17.05.2020
comment
@IsabelleNewbie Спасибо, Изабель, я посмотрю.   -  person David Tonhofer    schedule 17.05.2020
comment
Исключения в стиле ISO действительно предлагают то, что вы хотите. Условия исключения всегда имеют форму error/2. Первый аргумент - это термины в форме instantiation_error/0, syntax_error/1 или domain_error/2. Второй аргумент доступен для передачи дополнительной информации в любом стиле, который вы выберете. В общем, здесь больше значит меньше.   -  person repeat    schedule 19.05.2020
comment
@repeat Да, но ... к сожалению, функция выдачи библиотеки (ошибка) не позволяет установить этот параметр. Я просто хочу продиктовать! (Почему больше должно быть меньше? Это просто еще один термин, размер которого на самом деле не имеет значения - если только кто-то по какой-то причине не бросает безжалостно)   -  person David Tonhofer    schedule 20.05.2020
comment
Если вам нужно больше, чем предлагает эта библиотека, используйте throw/1 напрямую. В целом, простота - это хорошо. Делай одно дело и делай это хорошо ... в стиле UNiX!   -  person repeat    schedule 20.05.2020
comment
@repeat Роджер это!   -  person David Tonhofer    schedule 20.05.2020


Ответы (2)


Это зависит от системы Prolog и от доступных автоматизмов для индексации или от доступных директив для индексации. Например, SWI-Prolog имеет автоматическое глубокое индексирование, но имеет некоторые особенности, касающиеся автоматического индексирования с несколькими аргументами. Итак, для примера из мэдгена:

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.

person Mostowski Collapse    schedule 25.01.2021

Ваша интуиция относительно порядка параметров верна и может быть подтверждена простым экспериментом.

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

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

?- second(2,tag1(1)).
true ;
false.
person madgen    schedule 17.05.2020