【问题标题】:Change list of variables according to another list containing the index and atoms in prolog根据包含序言中索引和原子的另一个列表更改变量列表
【发布时间】:2020-04-29 08:45:01
【问题描述】:

我有一个变量列表 E 和一个列表 L,我想要一个像这样工作的谓词:

E=[A,B,C,D]
L=[(1,b),(3,m)]
solve(E,L).
E=[b,B,m,D]

基本上solve() 应该遍历列表L 并使用(a,b) 更改E 以将索引a 处的变量与原子B 统一。有没有办法做到这一点?

【问题讨论】:

    标签: list indexing prolog


    【解决方案1】:

    (名字不好的)solve/2 谓词的含义类似于“对于每一对(IndexElement),输入列表的Index-th 元素是Element”。您可能正在使用一个 Prolog 实现,它已经有一个名为 nth1/3 的谓词,它表示“ListIndex-th 元素是 Element”。例如,在 SWI-Prolog 中:

    ?- List = [A, B, C, D], nth1(3, List, this_is_the_third_element).
    List = [A, B, this_is_the_third_element, D],
    C = this_is_the_third_element.
    

    因此,您的谓词的替代实现只需为您的每个 (Index, Element) 对调用 nth1/3

    solve(_List, []).
    solve(List, [(Index, Elem) | Pairs]) :-
        nth1(Index, List, Elem),
        solve(List, Pairs).
    

    这样你就完成了:

    ?- E = [A, B, C, D], L = [(1, b), (3, m)], solve(E, L).
    E = [b, B, m, D],
    A = b,
    C = m,
    L = [(1, b),  (3, m)] ;
    false.
    

    请注意,此解决方案很简单,但输入列表的长度具有二次复杂度:nth1/3 可能必须访问整个 N 元素列表 N 次。在不太可能的情况下,您需要将此谓词用于某个较大程序的性能关键部分,请考虑在另一个答案中概述的更优化的解决方案。

    【讨论】:

    • 这很简单。我什至没有想到这一点。
    【解决方案2】:

    有什么办法吗?

    当然。正如他们在 Perl 中所说:“有不止一种方法可以做到这一点”。

    几个问题:

    不要使用(1,b)。改用惯用的-(1,b),写成1-b(一对)。这会给你一个配对列表:L=[1-b,3-m]。有一个专门处理此类对的库:https://www.swi-prolog.org/pldoc/man?section=pairs - 或者,您可以使用用 AVL 树实现的真实地图:https://www.swi-prolog.org/pldoc/man?section=assoc

    现在你只需要:

    1. 对列表进行排序,可能使用keysort:https://www.swi-prolog.org/pldoc/doc_for?object=sort/2https://www.swi-prolog.org/pldoc/doc_for?object=sort/4
    2. 从左到右遍历列表,保留当前索引,并在排序列表中的下一个键被命中时执行替换,否则仅保留列表中的现有术语。结果作为列表的头部进入累加器变量。
    3. 完成!越界索引等的特殊处理,通过抛出或失败适当处理。

    如何通过对的排序列表(我没有测试这个!):

    % case of Index hit:
    
    go_through([Index-Value|Rest],Index,InList,OutList) :-
       InList  = [I|Rest],
       OutList = [Value|More],
       succ(Index,NextIndex),
       go_through(Rest,NextIndex,Rest,More).
    
    % case of Index miss:
    
    go_through([NotYetIndex-Value|Rest],Index,InList,OutList) :-   
       NotYetIndex > Index,  % that should be the case
       InList  = [I|Rest],
       OutList = [I|More],
       succ(Index,NextIndex),
       go_through(Rest,NextIndex,Rest,More).
    
    go_through([],_,L,L). % DONE
    

    或者,您可以编写一个replace0 来替换列表中的索引,然后遍历L 列表。

    附录:使用 go_through 的工作代码

    其实包含一些细微之处

    another_vectorial_replace1(ListIn,ReplacePairs,ListOut) :-
       maplist([_,_]>>true,ListIn,ListOut),               % Bonus code: This "makes sure" (i.e. fails if not) 
                                                          % that ListIn and ListOut are the same length
       maplist([(A,B),A-B]>>true,ReplacePairs,RealPairs), % Transform all (1,b) into [1,b]
       maplist([K-_]>>integer(K),RealPairs),              % Make sure the RealPairs all have integers on first place   
       keysort(RealPairs,RealPairsSorted),                % Sorting by key, which are integers; dups are not removed!
       debug(topic,"ListIn: ~q",[ListIn]),
       debug(topic,"RealPairsSorted: ~q",[RealPairsSorted]),
       go_through(RealPairsSorted,1,ListIn,ListOut),
       debug(topic,"ListOut: ~q",[ListOut]).
    
    % Case of Index hit, CurIndex is found in the first "Replacement Pair"
    
    go_through([CurIndex-Value|RestPairs],CurIndex,ListIn,ListOut) :-
       !, % Commit to choice
       ListIn  = [_|Rest],
       ListOut = [Value|More],
       succ(CurIndex,NextIndex),
       go_through(RestPairs,NextIndex,Rest,More).
    
    % Case of Index miss:
    
    go_through([NotYetIndex-V|RestPairs],CurIndex,ListIn,ListOut) :-
       NotYetIndex > CurIndex,  % that should be the case because of sorting; fail if not
       !, % Commit to choice
       ListIn  = [X|Rest],
       ListOut = [X|More],
       succ(CurIndex,NextIndex),
       go_through([NotYetIndex-V|RestPairs],NextIndex,Rest,More).
    
    % Case of DONE with list traversal
    % Only succeed if there are not more pairs left (i.e. no out-of-bound replacements)
    
    go_through([],_CurIndex,L,L).
    
    % ===
    % Tests
    % ===
    
    :- begin_tests(another_vectorial_replace1).
    
    test(empty)  :- another_vectorial_replace1([],[],LO),
                    LO=[].
    
    test(nop_op) :- another_vectorial_replace1([a,b,c,d],[],LO),
                    LO=[a,b,c,d].
    
    test(one)    :- another_vectorial_replace1([a],[(1,xxx)],LO),        
                    LO=[xxx].
    
    test(two)    :- another_vectorial_replace1([a,b,c,d],[(4,y),(2,x)],LO),
                    LO=[a,x,c,y].
    
    test(full)   :- another_vectorial_replace1([a,b,c,d],[(1,e),(2,f),(3,g),(4,h)],LO),
                    LO=[e,f,g,h].
    
    test(duplicate_replacement,[fail]) :- another_vectorial_replace1([a],[(1,x),(1,y)],_). 
    test(out_of_bounds_high,[fail])     :- another_vectorial_replace1([a],[(2,y)],_).
    test(out_of_bounds_low,[fail])    :- another_vectorial_replace1([a],[(0,y)],_).
    
    :- end_tests(another_vectorial_replace1).
    
    rt :- debug(topic),run_tests(another_vectorial_replace1).
    

    附录 2

    使用maplist/Nfoldl/Nlibrary(assoc) 替换

    递归调用消失在幕后!

    https://github.com/dtonhofer/prolog_notes/blob/master/code/vector_replace0.pl

    【讨论】:

    • 感谢您的建议,列表已排序,因此无需执行第 1 步。由于英语不是我的母语,我只想确定一点:在 1-b 对中,1 是键,b 是值,对吗?我将如何使用这样的键作为列表的索引?感谢您的帮助!
    • @FranciscoCunha 是的,关键是第一位置,价值第二。我将添加一个基于library(assoc) 的示例,因为我一直想编写“列表中的矢量替换”只是有一个library(assoc) 直接解决了这个问题的脑电波(尽管可能具有统计上显着的计算开销?)
    • 您能否详细说明library(pairs) 部分? (我尝试实现pairsassoc 库,第一个似乎更适合我想要做的事情。)我已经将对列表分成两个列表:A 带有值,B 带有键.如何在更改相应索引处的原始列表的同时同时运行两个列表?
    • @FranciscoCunha 我为“遍历输入列表”添加了一些代码。你不需要分开 - 让 Prolog 在[Index-Value|Rest] 中进行反汇编。重要的部分是列表必须按 Key 升序排序。
    • 关于 go_through() 谓词的几个问题:1-当我第一次调用它时,索引中应该包含什么?2-当我尝试运行它时,我得到了 5 个不同的“单例变量” " 警告,3- 虽然我对函数的了解最少,但我不需要InListOutList,因为我只想更改一个列表,而不是创建一个新列表。你能测试一下代码吗?
    【解决方案3】:

    (以下假设对列表中的索引将按升序排序,如问题中的示例所示。)

    你说的可以写成一个连词

      E=[A,B,C,D], L=[(1,a),(3,c)], solve(E,L), E=[a,B,c,D].
    

    您打算在您寻求找到的solve/2 的正确定义下持有。但是这不是说吗

      E=[A|E2],    L=[(1,a)|L2], 
           E2=[B,C,D],      L2=[(3,c)],
                                   solve(E2,L2),     E2=[B,c,D], 
                                                E=[a|E2].
    

    ?虽然,这里有些不太合适。 E2 中的 c 出现在 second 位置,而不是 3rd,如其在 L2 中的条目所示。

    但很自然,L2 必须从 2 索引,因为它是从 1 索引的 L 的尾部。所以我们必须明确this

      E=[A,B,C,D], L=[(1,a),(3,c)], solve(E,L), E=[a,B,c,D]
    ==
      E=[A,B,C,D], L=[(1,a),(3,c)], solve(E,1,L), E=[a,B,c,D]          % starting index 1
    ==
      E=[A|E2],    L=[(1,a)|L2], 
           E2=[B,C,D],      L2=[(3,c)],
                                   solve(E2,2,L2), E2=[B,c,D], E=[a|E2]
    

    必须,现在可以,坚持。但是a 是从哪里来的,在E 中?我们在这里的真正意思是

      E=[A|E2],    L=[(1,a)|L2],
                   p( (1,a),                1,                    a),   % index match
           E2=[B,C,D],      L2=[(3,c)],
                                   solve(E2,2,L2), E2=[B,c,D],          % starting index 2
                                                               E=[a|E2]
    

    p/3 定义为

    p( (I,A), I, A).
    

    所以它也必须保持这一点

           E2=[B|E3],       L2=[(3,c)],
                        \+ p(   (3,c),      2,         c),              % index mismatch
                 E3=[C,D],     L3=L2,
                                   solve(E3,3,L3), E3=[c,D], E2=[B|E3]
    

    L2 在这一步 (L3=L2) 被遍历,因为p( (3,c), 2, c) 成立。

    您看到solve/3递归 定义是如何在此处显示的吗?你能完成它吗?

    【讨论】:

      猜你喜欢
      • 2017-03-10
      • 2020-06-30
      • 2017-12-08
      • 2014-01-12
      • 2023-02-23
      • 1970-01-01
      • 2016-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多