Пролог называет неправильное правило. Не возвращается правильно - PullRequest
1 голос
/ 09 февраля 2012

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

% 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, это работает.

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

Ответы [ 4 ]

1 голос
/ 30 июня 2015

Сохранение с использованием встроенных предикатов 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.

Да! * 1018Что делать, если индекс не конкретное целое число, а несвязанная логическая переменная? что работает?

?- 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.

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

0 голосов
/ 09 февраля 2012

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

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

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

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

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

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
?- 

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

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

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

0 голосов
/ 12 февраля 2012
replaceAtIndex(I, Value, List, Result) :-
    RI is I-1,
    findall(Before, (nth0(B, List, Before), B < RI), Before),
    findall(After, (nth0(A, List, After), A > RI), After),
    append([Before, [Value], After], Result).


?- replaceAtIndex(3,0,[1,2,3,4,5,6], L).
L = [1, 2, 0, 4, 5, 6].
0 голосов
/ 09 февраля 2012

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

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

Когда я заменяю _index_in_list на постоянную 111, это работы.

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

...