按索引号在列表之间移动元素?

时间:2018-02-15 22:10:17

标签: list prolog element move

我正在尝试在LoL(list-of-list)结构中的列表之间移动元素。 我可以弄清楚如何选择列表并移动元素,但我迷失了如何重建生成的LoL。 这是部分功能:

 move(From, To, State=[P1,P2,P3], NewState=[NP1, NP2, NP3]) :- 
    nth1(From, State, FL), nth1(To, State, TL), move_elem(FL, TL, NewFL, NewTL),
   .....whats here?...
   .. NewState should be build out of NewTL,NewFL and one of P1,P2,P3...
   .. the order of the lists should be preserved..

move_elem/4已实施。 From& To是整数,并指定将参与移动操作的位置列表。

目前LoL是3个列表的列表,但我希望将来能够参数化列表数量。

  

状态是移动前的LoL,NewState是移动后的LoL。

?- move_elem([1,2,3], [4,5,6], F,T).
F = [2, 3],
T = [1, 4, 5, 6].

nth1 / 3似乎工作正常。

?- L=[[1,2],[3,4],[5,6]], nth1(2,L,El).
L = [[1, 2], [3, 4], [5, 6]],
El = [3, 4].

move()shold将元素从三个列表中的一个移到另一个列表。 From和To是列表索引f.e。

LoL = [[1,2],[3,4],[5,6]]
move(1,3, LoL, NewLoL)
NewLoL = [[2],[3,4],[1,5,6]]

move(2,1, LoL, NewLoL)
NewLoL = [[3,1,2],[4],[1,5,6]]

将top元素从list-1移动到list-3。

3 个答案:

答案 0 :(得分:1)

您可以通过以下方式实施move/4

appendHead(T,H,[H|T]).
removeHead([_|T],T).

insert(_,_,_,_,_,[],[]).
insert(C,I2,L1,L2,C,[_|TI],[L1|TO]):-
    C1 is C+1,
    insert(C,I2,L1,L2,C1,TI,TO).
insert(I1,C,L1,L2,C,[_|TI],[L2|TO]):-
    C1 is C+1,
    insert(I1,C,L1,L2,C1,TI,TO).
insert(I1,I2,L1,L2,C,[HI|TI],[HI|TO]):-
    I1 \= C,
    I2 \= C,
    C1 is C+1,
    insert(I1,I2,L1,L2,C1,TI,TO).

move(I1,I2,LIn,LOut):-
    nth1(I1,LIn,L1),
    nth1(I2,LIn,L2),
    nth1(1,L1,E1),
    removeHead(L1,L1R),
    appendHead(L2,E1,L2F),
    insert(I1,I2,L1R,L2F,1,LIn,LOut).

?- LoL = [[1,2],[3,4],[5,6]], move(1,3, LoL, NewLoL).
LoL = [[1, 2], [3, 4], [5, 6]],
NewLoL = [[2], [3, 4], [1, 5, 6]].
false.

?- LoL = [[2], [3, 4], [1, 5, 6]], move(2,1, LoL, NewLoL).
LoL = [[2], [3, 4], [1, 5, 6]],
NewLoL = [[3, 2], [4], [1, 5, 6]].
false.

?- LoL = [[1,2],[3,4],[5,6]], move(2,1, LoL, NewLoL).
NewLoL = [[3,1,2],[4],[1,5,6]].
false.

如果您想阻止回溯,只需在!的每个定义后添加一个剪切insert/4(您将无法获得false)。

答案 1 :(得分:1)

使用length/2append/3

move(From, To, State, NewState):-
  length([_|HState], From),
  length([_|HNewState], To),
  append(HState, [[Item|L]|TState], State),
  append(HState, [L|TState], MState),
  append(HNewState, [NL|TNewState], MState),
  append(HNewState, [[Item|NL]|TNewState], NewState).

我们的想法是使用length/2生成长度为From-1且长度为To-1的另一个未实例化变量的列表(因此我们从长度From和To的列表中跳过一个元素)。

然后append/3可用于将State分成两部分或连接两个列表。

第一次追加调用会将状态拆分为From-1元素的HState列表,第二个列表包含其余元素。其余部分的第一个元素进一步分为两部分(要移动的项目和该元素的其余部分)。

第二次追加调用加入了两个部分,不包括要移动的项目。

第三次和第四次追加调用重复了这个想法,虽然这次他们被用来将移动的项目添加到目标位置。

答案 2 :(得分:0)

Prolog进行深度优先搜索,这意味着它将尝试通过探索解决方案的所有可能路径来实现目标。每当有多条路径可用时,它会创建一个选择点,然后从上到下的选项向下工作。这意味着,如果选择立即失败,则会调用下一个选项。这使您可以非常清楚地创建路径,并可以选择制作。包括列表在内的所有原子在Prolog中都是有效的。你想要做的是从第一个列表构建第二个列表,可能是以不同的顺序或更少的元素,更多的元素,替换的元素等。

我使用矩阵中的单元格(点(X,Y),值)坐标替换行中的单元格作为示例。重建列表的三个选择。

  1. 已到达列表的末尾,这是结束条件并结束新列表。
  2. 检查列表的第一个单元格,并且坐标与替换不匹配,因此将其放置在新列表中。
  3. 列表的第一个单元格与坐标匹配,因此将元素放在新列表中,并通过添加剩余的未经检查的元素作为尾部将其关闭。
  4. 这导致代码:

    %coordinate not found, return the list as is
    replace_cell(_,_,[],[]).
    %no match, X already extracted from Element to reduce calls 
    replace_cell(X, Element, [RowElement|Tail], [RowElement|NewTail]) :-
        RowElement \= cell(point(X,_),_),
        replace_cell(X, Element, Tail, NewTail).
    %match
    replace_cell(X, Element, [cell(point(X,_),_)|Tail], [Element|Tail]).
    

    这是您可能习惯的标准递归。设定目标,达到答案,Prolog开始退回所做的调用,填写我们在退回时打开的变量。然后那些填充的变量成为下一个返回的调用的输出。

    然后使用多层列表进行操作,我们只需要确定哪个列表需要传递给下一个更具体的调用。在这个矩阵示例中,它是一个正方形,因此所有列共享一个X,所有行共享一个Y坐标。为了确定我们需要哪一行,我们可以将整个矩阵提供给一个检查Y的简单谓词。注意我们实际上还没有修改矩阵,只是识别行。这主要是为了保持代码的可读性。

    %are there any rows left?
    move_to_row(_, [], []).
    %is element's Y equal to row Y?
    move_to_row(Y, [Row|_], Row) :- Row = [cell(point(_,Y),_)|_].
    %move to next row
    move_to_row(Y,[_|Tail], Row) :- move_to_row(Y,Tail,Row).
    

    在替换行中的元素之后,由于我们同时拥有旧行和新行,我们可以识别旧行并使用新行重建矩阵。

    replaceElement(_,_,[],[]).
    replaceElement(Replacable, Replacement, [Item|List], [Replacable|NewList] ) :-
       Item \= Replacable,
       replaceElement(Replacable, Replacement, List, NewList),
    replaceElement(Replacable, Replacement, [Replacable|List], [Replacement|List] ) :- 
       !.
    

    如此有效,列表中的位置由我们在下行时逐步浏览列表的顺序保留,然后新的列表在上升的过程中重建。

    对于列表[1,2,3][Head|Tail]为我们提供了Head = 1, Tail = [2,3]。如果我们再次致电[Head|Tail],它会给我们Head = 2, Tail = [3]。在回来的路上,恰好相反,头部附加在尾部的前面。通过将每个列表层可视化为其自己的可识别类型并为其提供自己的标识符,无论您想要嵌套列表中的列表数量多少,都应该非常简单地通过它们。矩阵是一个2d矩形,但是例如通过将cell内的坐标作为列表并添加Z轴,它可以很容易地变成一个立方体或更复杂的三维以上。