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

当我调试它的调用时,索引为 111。
当我用_index_in_list常量 111 替换它时,它可以工作。

任何人都可能知道为什么会发生这种情况?

4

4 回答 4

1

Preserve by using the builtin predicates same_length/2, length/2 and append/3!

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

First, let's run the sample query that @magus used in a previous answer to this question:

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

Does it work when the list items are instantiated later?

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

Yes! What if the index isn't a concrete integer, but an unbound logical variable? Does that work?

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

It does! With monotone code, we get logically sound answers even with very general queries.

于 2015-06-30T04:33:00.530 回答
0

我怀疑你误解了回溯发生的方向。

第一个“基本”规则将首先尝试调用任何replaceAtIndex/4. 如果失败,由于与第一条规则的“头”的调用的不可统一性,那么 Prolog 引擎将回溯到第二条规则。[统一失败可能是由于第一个参数(索引)不同于 1 或第三个参数不是非空列表。]

回溯永远不会朝另一个方向发展。如果尝试第二条规则但失败,则调用失败。

当然,递归定义使事情变得复杂。应用第二条规则的成功需要对 的新调用replaceAtIndex/4,就 Prolog 引擎而言,它必须开始尝试通过从第一条规则开始来满足该目标。

我建议在第一条规则中添加一个削减,因为通过构造,如果第一条规则成功,第二条规则将永远不会成功。但这只是一个效率问题……为什么要让一个永远不会产生任何进一步解决方案的选择点打开?

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

补充:我确认您的代码在 Amzi 中有效!Prolog 调用如下:

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

于 2012-02-09T16:10:58.063 回答
-1

尝试_index_in_list设置为1. 那么第一条规则不会被调用吗?

_index_in_list如果你的 is肯定会调用第二条规则的原因111是因为111大于1?第一条规则只处理1- 正如第一个参数所说。

当我用_index_in_list常量替换它时,111它可以工作。

什么?你的意思是第一条规则被调用?怎么可能,第一个参数是1.

于 2012-02-09T12:41:25.313 回答
-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].
于 2012-02-12T08:21:37.107 回答