【发布时间】: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 统一。有没有办法做到这一点?
【问题讨论】:
我有一个变量列表 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 统一。有没有办法做到这一点?
【问题讨论】:
(名字不好的)solve/2 谓词的含义类似于“对于每一对(Index,Element),输入列表的Index-th 元素是Element”。您可能正在使用一个 Prolog 实现,它已经有一个名为 nth1/3 的谓词,它表示“List 的 Index-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 次。在不太可能的情况下,您需要将此谓词用于某个较大程序的性能关键部分,请考虑在另一个答案中概述的更优化的解决方案。
【讨论】:
有什么办法吗?
当然。正如他们在 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
现在你只需要:
如何通过对的排序列表(我没有测试这个!):
% 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 列表。
其实包含一些细微之处
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).
使用maplist/N、foldl/N 和library(assoc) 替换
递归调用消失在幕后!
https://github.com/dtonhofer/prolog_notes/blob/master/code/vector_replace0.pl
【讨论】:
library(assoc) 的示例,因为我一直想编写“列表中的矢量替换”只是有一个library(assoc) 直接解决了这个问题的脑电波(尽管可能具有统计上显着的计算开销?)
library(pairs) 部分? (我尝试实现pairs 和assoc 库,第一个似乎更适合我想要做的事情。)我已经将对列表分成两个列表:A 带有值,B 带有键.如何在更改相应索引处的原始列表的同时同时运行两个列表?
[Index-Value|Rest] 中进行反汇编。重要的部分是列表必须按 Key 升序排序。
InList 和OutList,因为我只想更改一个列表,而不是创建一个新列表。你能测试一下代码吗?
(以下假设对列表中的索引将按升序排序,如问题中的示例所示。)
你说的可以写成一个连词
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 的递归 定义是如何在此处显示的吗?你能完成它吗?
【讨论】: