Пролог вызывает неправильное правило. Не правильно отступает

Что случилось?
У меня очень странные проблемы с Прологом.
Рекурсивное правило замены элемента в списке по заданному индексу не всегда работает.
Мое правило выглядит так:

% Base rule - Stops when index is 1 and replaces the head with the element.
replaceAtIndex(1, _element, [_|_tail], [_element|_tail]).

% Recursive rule - Enter recursion for tail as long as index is larger than 1.
replaceAtIndex(_index, _element, [_head|_tail], [_head|_new_tail]):-
    _index > 1,
    _new_index is _index - 1,
    replaceAtIndex(_new_index, _element, _tail, _new_tail).

Когда я использую отладчик внутри своей программы, я вижу, что он всегда вызывает второе правило независимо от индекса, но когда я выполняю точно такую ​​же команду вне своей программы, она работает отлично. Он достигает индекса 1, но вызывает второе правило и НЕ откатывается и не пытается выполнить первое правило, и все его резервное копирование завершается неудачно...

Правило, вызывающее replaceAtIndex, выглядит так:

level_replace_block_value(_x, _y, _value):-
    current_level(_level_number, _width, _height, _blocks, _drawX, _drawY),
    coordinates_to_index(_x, _y, _index),
    _index_in_list is _index + 1, % the replaceAtIndex is not 0 terminated
    replaceAtIndex(_index_in_list, _value, _blocks, _new_blocks),
    retractall(current_level(_,_,_,_,_,_)),
    assert(current_level(_level_number, _width, _height, _new_blocks, _drawX, _drawY),
    graphics_update_block_value(_x, _y).

Когда я отлаживаю его вызов с индексом 111.
Когда я заменяю _index_in_list константой 111, это работает.

Кто-нибудь может подсказать, почему так происходит?


person YLivay    schedule 09.02.2012    source источник
comment
Можете ли вы использовать какой-нибудь вызов replaceAtIndex, который действительно ведет себя так, как вы описываете? В идеале без использования level_replace_block_value. Может быть, проблема в том, что две ссылки на _tail (или _element) не могут быть объединены?   -  person svick    schedule 09.02.2012
comment
Хорошо, я отследил его в отладчике до индекса 3. Упс, секундочку...   -  person YLivay    schedule 09.02.2012
comment
Хорошо, я отследил его в отладчике до индекса 3. Вот как это выглядит: paste.ubuntu .com/835140. Когда я вызываю его вне своей программы, это выглядит так: paste.ubuntu.com/835141 и отладка выглядит так: paste.ubuntu.com/835145   -  person YLivay    schedule 09.02.2012
comment
Это очень странно. Какую систему Prolog вы используете?   -  person twinterer    schedule 09.02.2012
comment
Очень странно. Можете ли вы воспроизвести это с помощью более простого правила? Что произойдет, если у вас есть один факт p(1)., и вы запросите ?- X is 2 - 1, p(X).?   -  person Nick Barnes    schedule 09.02.2012
comment
Просмотр этого .pro: paste.ubuntu.com/835202 и запуск doReplace(2, _new_list). дает _new_list = [0,1,10,3,4,5] ; no ?- Не удается воссоздать это:   -  person YLivay    schedule 09.02.2012
comment
Редактирование правила, включающее максимальный счет вверх, а не вниз до 1, также не работает...   -  person YLivay    schedule 09.02.2012
comment
Попробуйте объединить два предложения и использовать if-then-else: replaceAtIndex(I,...) :- ( I == 1 -> ... ; ... ). и посмотрите, приведет ли это к другому поведению.   -  person twinterer    schedule 09.02.2012
comment
Вы должны удалить префикс подчеркивания из переменных, которые вам действительно нужны, особенно в _index... возможно, ваш интерпретатор выполняет глупую оптимизацию. Это стандартное соглашение для НЕИСПОЛЬЗУЕМЫХ переменных.   -  person Volker Stolz    schedule 09.02.2012
comment
Я не верю. Переписав правило следующим образом: paste.ubuntu.com/835252 получается следующее: paste.ubuntu.com/835253 В 1 ЕСТЬ 1?!?! Он также терпит неудачу на 1 == 1   -  person YLivay    schedule 09.02.2012
comment
А как насчет других параметров? Я предполагаю, что это не ошибка 1 == 1, а один из других параметров replaceAtIndex. Попробуйте написать более простой код только с первым параметром и протестировать его.   -  person therobyouknow    schedule 09.02.2012
comment
Код, который вы только что разместили в pastebin, неверен. Используйте is/2 только для арифметической оценки. _new_tail is tail не получится.   -  person twinterer    schedule 09.02.2012
comment
Да, я уже исправил это другим субправилом list_is(_list, _list). Я счастлив сказать, что теперь он работает правильно, но я действительно думаю, что он должен был работать нормально! Почему обратная связь не удалась???   -  person YLivay    schedule 09.02.2012
comment
Попробуйте это: replaceAtIndex(I,E,[H|T],[NH|NT]) :- ( I==1 -> NH=E,NT=T ; NH=H,I1 is I-1,replaceAtIndex(I1,E,T,NT) ).   -  person twinterer    schedule 09.02.2012
comment
Любая идея, почему числа, которые я прохожу, могут не быть числами? 1 должен возвращать истину, если только они не являются целыми числами, а литералами. Как убедиться, что я работаю с числами?   -  person YLivay    schedule 09.02.2012
comment
В вашем исходном сообщении индексы должны быть числами, иначе вычисление следующего индекса не удастся. Вопрос скорее в том, почему 1 в первом предложении не было числом? То есть, почему вызов с индексом 1 не совпал с заголовком первого предложения? Я никогда не использовал Amzi, ​​поэтому понятия не имею, почему он интерпретирует его как что-то другое.   -  person twinterer    schedule 09.02.2012
comment
попробуйте номер / 1. Итак, является ли префикс подчеркивания некоторым соглашением amzi! пролог? потому что, если он ведет себя как в большинстве реализаций пролога (неиспользуемые переменные), я бы сказал, что это проблема   -  person Thanos Tintinidis    schedule 09.02.2012
comment
@thanosQR: проблема может быть в подчеркивании, но, например, ECLiPSe и SWI будут работать с префиксами подчеркивания. Только для чистых символов подчеркивания генерируются новые переменные.   -  person twinterer    schedule 09.02.2012
comment
Амзи! не должно быть проблем с подчеркиванием. Числа по некоторым причинам рассматриваются как литералы. По крайней мере, это мое предположение... Я попробую обозначение числа/1.   -  person YLivay    schedule 09.02.2012


Ответы (3)


Сохраняйте logical-purity, используя встроенные предикаты same_length/2, length/2 и append/3!

replace_at(I,X,Xs0,Xs2) :-
   same_length(Xs0,Xs2),
   append(Prefix,[_|Xs1],Xs0),
   length([_|Prefix],I),
   append(Prefix,[X|Xs1],Xs2).

Во-первых, давайте запустим пример запроса, который @magus использовал в предыдущем ответе на этот вопрос:

?- replace_at(3,0,[1,2,3,4,5,6],Xs).
Xs = [1,2,0,4,5,6] ;
false.

Работает ли это, когда элементы списка создаются позже?

?- replace_at(3,0,[A,B,C,D,E,F],Xs), A=1,B=2,C=3,D=4,E=5,F=6.
A = 1, B = 2, C = 3, D = 4, E = 5, F = 6, Xs = [1,2,0,4,5,6] ;
false.

Да! Что, если индекс представляет собой не конкретное целое число, а несвязанную логическую переменную? Это работает?

?- replace_at(I,x,[_,_,_,_,_],Ys).
I = 1, Ys = [ x,_B,_C,_D,_E] ;
I = 2, Ys = [_A, x,_C,_D,_E] ;
I = 3, Ys = [_A,_B, x,_D,_E] ;
I = 4, Ys = [_A,_B,_C, x,_E] ;
I = 5, Ys = [_A,_B,_C,_D, x] ;
false.

Да! С монотонным кодом мы получаем логически обоснованные ответы даже на очень общие запросы.

person repeat    schedule 30.06.2015
comment
К какой части Вопроса вы обращаетесь? Это кажется совершенно полемическим и не направленным (через три года после факта) на вопросы, о которых спрашивали. - person hardmath; 02.07.2015

Я подозреваю, что вы неправильно понимаете направление, в котором будет происходить откат.

Первое «базовое» правило будет пробоваться первым для любого вызова replaceAtIndex/4. Если это не удается из-за неунифицируемости вызова с «головой» первого правила, то механизм Пролога возвращается ко второму правилу. [Ошибка объединения может произойти из-за того, что первый аргумент (индекс) отличен от 1, или из-за того, что третий аргумент не является непустым списком.]

Откат никогда не идет в другом направлении. Если второе правило опробовано и терпит неудачу, вызов терпит неудачу.

Конечно, все усложняется рекурсивным определением. Успех применения второго правила влечет за собой новый вызов replaceAtIndex/4, который, насколько это касается механизма Prolog, должен начать попытки выполнить эту цель, начиная с первого правила.

Я бы предложил добавить сокращение к первому правилу, так как по построению второе правило никогда не будет успешным, если первое правило будет успешным. Но это всего лишь вопрос эффективности... зачем оставлять открытой точку выбора, которая никогда не приведет к каким-либо дальнейшим решениям?

replaceAtIndex(1, _element, [_|_tail], [_element|_tail]) :- !.

Добавлено: я подтвердил, что ваш код работает в Amzi! Пролог с таким вызовом:

?- replaceAtIndex(3, 0, [1,2,3,4,5,6], L).

L = [1, 2, 0, 4, 5, 6] ;
no
?- 

Но, конечно, вы также видите успех, когда код вызывается в автономном/интерпретируемом режиме.

Поэтому я должен подозревать, что передаваемый аргумент «индекс» не является целым числом, а скорее числом с плавающей запятой. Целое число 1 не будет объединяться с числом с плавающей запятой 1.0, даже если они математически равны.

Вы можете использовать предикат is_integer/1, чтобы проверить это в Amzi! Пролог. Функцию integer(X) можно использовать для преобразования вещественного числа 1,0 в целое число 1 путем усечения (несуществующей) дробной части.

person hardmath    schedule 09.02.2012
comment
Смотрите третий комментарий к вопросу. Он опубликовал две трассировки, которые показывают разное поведение. В одном случае первое правило по необъяснимым причинам не используется (т. е. голова не соответствует), и большой вопрос, почему. - person twinterer; 09.02.2012
comment
hardmatch большое спасибо за подробный ответ! Я очень хорошо знаю, как работает возврат, поэтому я не понимаю, почему он не пытается выполнить первое (базовое) правило. Я знаю, что список не пуст, поэтому проблема должна быть в 1. Дело в том, что я не передаю ему числа с плавающей запятой, а в правилах расчета индекса у меня есть число (_result) в качестве последней строки, поэтому оно должно быть целым числом. Я мог бы опубликовать всю свою программу, чтобы вы могли запустить ее самостоятельно, это поможет? - person YLivay; 09.02.2012
comment
@YarivLivay: Вы можете опубликовать это здесь, но вы также можете опубликовать код на Amzi! Пролог форум. Я обещаю доложить здесь о том, что окажется объяснением в любом случае. - person hardmath; 09.02.2012

Попробуйте _index_in_list установить на 1. Разве тогда не будет вызвано первое правило?

Конечно, причина, по которой вызывается второе правило, если ваше _index_in_list равно 111, заключается в том, что 111 больше, чем 1? Первое правило касается только 1 - как говорит первый параметр.

Когда я заменяю _index_in_list константой 111, все работает.

Какая? Вы имеете в виду, что вызывается первое правило? Как же так, первый параметр 1.

person therobyouknow    schedule 09.02.2012
comment
Когда я меняю вызов в своей программе с этого replaceAtIndex(_index_in_list, _value, _blocks, _new_blocks), на этот replaceAtIndex(111, _value, _blocks, _new_blocks),, он работает, хотя _index_in_list равен 111! Посмотрите мой последний комментарий тоже, я поставил дамп отладки. - person YLivay; 09.02.2012