2012-02-09 4 views
1

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

% 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). 

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

Любой может иметь ключ к пониманию, почему это происходит?

+0

Можете ли вы нас вызвать 'replaceAtIndex', который действительно ведет себя так, как вы описываете? В идеале без использования 'level_replace_block_value'. Может быть, проблема в том, что две ссылки на '_tail' (или' _element') не могут быть объединены? – svick

+0

Хорошо, я отследил его в отладчике, чтобы индексировать его. Упс всего лишь секунду ... – YLivay

+0

Хорошо, я отследил его в отладчике до индекса 3. Вот как это выглядит: http: // paste.ubuntu.com/835140/. Когда я называю это вне моей программы, это выглядит так: http://paste.ubuntu.com/835141/, и отладка выглядит следующим образом: http://paste.ubuntu.com/835145/ – YLivay

ответ

-1

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

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

Когда я заменить _index_in_list с постоянной 111 ней работ.

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

+0

Когда я меняю вызов в своей программе с помощью этого 'replaceAtIndex (_index_in_list, _value, _blocks, _new_blocks),' to this 'replaceAtIndex (111, _value, _blocks, _new_blocks), 'работает, хотя _index_in_list равно 111! Посмотрите также на мой последний комментарий, я положил дамп отладки. – YLivay

0

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

Первое правило «базы» будет проверено первым для любого вызова 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» не является целым числом, а скорее числом с плавающей запятой. Integer 1 не будет объединяться с плавающей точкой 1.0, даже если они математически равны.

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

+0

См. Третий комментарий к вопросу. Он опубликовал две следы, которые показывают другое поведение. В одном случае первое правило необъяснимо не проверяется (т. Е. Голова не совпадает), и поэтому возникает большой вопрос. – twinterer

+0

hardmatch спасибо за подробный ответ! Я очень хорошо знаю, как работает backtracking, вот почему я не понимаю, почему он не пробовал первое (базовое) правило. Я знаю, что список не пуст, поэтому проблема должна быть проблемой. Дело в том, что я не передаю ему плавающие точки, а в правилах, которые вычисляют индекс, у меня есть номер (_result) в качестве последней строки, поэтому он должен быть целым числом. Я мог бы опубликовать всю свою программу, чтобы вы могли ее запустить самостоятельно, это поможет? – YLivay

+0

@YarivLivay: Вы можете разместить его здесь, но вы также можете опубликовать код в Amzi! Пролог форум. Я обещаю сообщить здесь о том, что оказывается объяснением, в любом случае. – hardmath

-1
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]. 
1

заповедника с помощью встроенного предиката 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 используется в a previous answer на этот вопрос:

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

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

+0

В какой части вопроса вы обращаетесь? Это кажется вполне полемическим, а не (через три года после факта) направлением на вопросы, о которых спрашивали. – hardmath

Смежные вопросы