我希望有一个Prolog谓词可以替换指定索引处的列表中的元素。
示例:
% replace(+List,+Index,+Value,-NewList).
?- L=[a,b,c,d], replace(L,1,z,L2).
L2 = [a,z,c,d]
我不知道该怎么做。谢谢你的帮助!卢瓦克。
答案 0 :(得分:13)
我会给你一个基本案例,我认为你应该能够轻松地完成递归案例:
replace([_|T], 0, X, [X|T]).
修改强>
现在op解决了它,我将添加递归的情况:
replace([H|T], I, X, [H|R]):- I > 0, I1 is I-1, replace(T, I1, X, R).
<强> EDIT2:强>
这应该在一个超出界限的情况下返回原始列表,因为@GeorgeConstanza在评论中提出:
replace([_|T], 0, X, [X|T]).
replace([H|T], I, X, [H|R]):- I > -1, NI is I-1, replace(T, NI, X, R), !.
replace(L, _, _, L).
如果有一个良好的入境替换,它基本上利用了cut操作符来获得第三个回退条款。
答案 1 :(得分:5)
来自fortran的回答没关系,但在SWI-Prolog结构中有无限的arity,所以这应该有效:
replace([_|T], 0, X, [X|T]).
replace([H|T], I, X, [H|R]) :-
I > 0,
I1 is I - 1,
replace(T, I1, X, R).
replace1(L, I, X, R) :-
Dummy =.. [dummy|L],
J is I + 1,
nb_setarg(J, Dummy, X),
Dummy =.. [dummy|R].
tr(Method, K) :-
length(L, K),
K1 is K - 1,
time(call(Method, L, K1, test, R)),
assertion(nth1(K, R, test)).
但令我惊讶的是:
?- % /home/carlo/prolog/replace.pl compiled 0,00 sec, 2,280 bytes
?- tr(replace,2000000).
% 3,999,999 inferences, 2,123 CPU in 2,128 seconds (100% CPU, 1884446 Lips)
true .
?- tr(replace1,2000000).
% 5 inferences, 1,410 CPU in 1,414 seconds (100% CPU, 4 Lips)
true.
?- tr(replace,4000000).
% 7,999,999 inferences, 3,510 CPU in 3,520 seconds (100% CPU, 2279267 Lips)
true .
?- tr(replace1,4000000).
% 5 inferences, 2,825 CPU in 2,833 seconds (100% CPU, 2 Lips)
true.
?- tr(replace,5000000).
% 9,999,999 inferences, 3,144 CPU in 3,153 seconds (100% CPU, 3180971 Lips)
true .
?- tr(replace1,5000000).
% 5 inferences, 4,476 CPU in 4,486 seconds (100% CPU, 1 Lips)
ERROR: =../2: Arguments are not sufficiently instantiated
^ Exception: (9) setup_call_catcher_cleanup(system:true, prolog_statistics:catch(user:call(replace1, [_G1, _G4, _G7, _G10|...], 4999999, test, _G15000005), _G15000021, (report(t(1324124267.2924964, 18.892632697, 28490132), 10), throw(_G15000021))), _G15000145, prolog_statistics: (_G15000032=true)) ? abort
% Execution Aborted
我的第一次尝试(K = 10000000)杀死了这个过程! 所以,由于我不喜欢,试图获得一些表现,我最终填写了一份错误报告给SWI-Prolog邮件列表......
编辑:在发布到SWI-Prolog邮件列表和(快速!)更正后,我已经重建了,这里是版本会计用于内存使用的提示(现在全部都是ISO标准代码!)。由于不寻常的大值,之前需要堆栈增长指令:
?- prolog_stack_property(global,limit(L)), N is L*2, set_prolog_stack(global,limit(N)).
N = 536870912.
以下是更新程序:
replace2(L, I, X, R) :-
Dummy =.. [dummy|L],
J is I + 1,
setarg(J, Dummy, X),
Dummy =.. [dummy|R].
和测试:
?- tr(replace,10000000).
% 19,999,999 inferences, 5.695 CPU in 5.719 seconds (100% CPU, 3511942 Lips)
true .
?- tr(replace2,10000000).
% 5 inferences, 2.564 CPU in 2.571 seconds (100% CPU, 2 Lips)
true.
代码更快,但请注意Jan对我邮件的评论:
归结为= ..(+, - )中的错误处理。固定。 B.t.w.一世 认为这是完成工作的可怕方式。即使你想要 这样做,只需使用setarg / 3而不是nb_setarg / 3。该 后者应该是最后的手段。此方法使用更多内存 因为它需要巨大的术语和列表。最后,算子 (名称/ arity对)目前没有回收,所以你创建一个 每个替换一个具有此长度的列表的对象 从未使用过。
答案 2 :(得分:4)
显然,使用@fortran的递归替换是最佳选择。但实际使用这太疯狂/慢了吗?
replace(List, Idx, With, ListOut) :-
length(Idx, Before),
append(Before, After, List),
( After=[_Discard|Rest]
-> true
; Rest=[]
),
append(Before, [With|Rest], ListOut).
基本上你制作一个大小为Idx的空白数组并将其绑定到输入列表。然后丢弃该项目并将两个列表与夹在中间的替换元素绑定在一起。
如果您尝试设置N个元素列表的idx N(索引为0),则可以进一步简化此操作。
replace(List, Idx, With, ListOut) :-
length(Idx, Before),
append(Before, [_Discard|Rest], List),
append(Before, [With|Rest], ListOut).
答案 3 :(得分:3)
实际上,没有人应该永远使用任何相当长的IMO的简单列表执行此操作,因为每次更新都会占用 O(n)
新空间。通过setarg/nb_setarg
直接set_once / update将占用 0
新空间,并使用列表的二叉树表示O(log(n))
新空间。替换也可以保存在单独的字典中,字典本身维护为树(因为它需要增长)。 chunked list (作为in here)可以在树中保存大块,每个块都是一个固定大小的复合词,可以通过setarg/nb_setarg
直接设置/更新,并添加新的块。树根据需要。
即使没有更新,只需访问普通列表就会很慢,O(n)
时间,快速转换任何算法二次。列表只是很好,或作为家庭作业。
答案 4 :(得分:3)
如果我们使用same_length/2
,append/3
和length/2
,我们就不需要编写递归代码了:
list_nth0_item_replaced(Es, N, X, Xs) :- same_length(Es, Xs), append(Prefix, [_|Suffix], Es), length(Prefix, N), append(Prefix, [X|Suffix], Xs).
OP给出的示例查询:
?- list_nth0_item_replaced([a,b,c,d], 1, z, Xs).
Xs = [a,z,c,d]
; false.
这也适用于“另一个方向”!
?- list_nth0_item_replaced(Xs, 1, z, [a,z,c,d]).
Xs = [a,_A,c,d]
; false.
更好的是,我们甚至不需要指定具体的索引:
?- list_nth0_item_replaced(Es, N, X, [a,z,c,d]).
N = 0, X = a, Es = [_A, z, c, d]
; N = 1, X = z, Es = [ a,_A, c, d]
; N = 2, X = c, Es = [ a, z,_A, d]
; N = 3, X = d, Es = [ a, z, c,_A]
; false.
?- list_nth0_item_replaced([a,b,c,d], N, X, Xs).
N = 0, Xs = [X,b,c,d]
; N = 1, Xs = [a,X,c,d]
; N = 2, Xs = [a,b,X,d]
; N = 3, Xs = [a,b,c,X]
; false.
答案 5 :(得分:3)
我提出的另一种方式,我认为是正确的(?)。我不知道运行时的复杂性。
replace(I, L, E, K) :-
nth0(I, L, _, R),
nth0(I, K, E, R).
用法:
?- replace(2, [1, 2, 3, 4, 5], 10, X).
X = [1, 2, 10, 4, 5].
答案 6 :(得分:2)
所提供的代码in this previous answer 非常多才多艺 - 感谢clpfd。
有缺点吗?是的,有一个缺点:效率低下!
在这个答案中,我们提高了性能和保持多功能性。
:- use_module(library(clpfd)).
在定义谓词fd_length/2
:
list_nth0_item_replaced__NEW(Es, N, X, Xs) :-
list_index0_index_item_replaced(Es, 0,N, X, Xs).
list_index0_index_item_replaced([_|Es], I ,I, X, [X|Es]).
list_index0_index_item_replaced([E|Es], I0,I, X, [E|Xs]) :-
I0 #< I,
I1 #= I0+1,
list_index0_index_item_replaced(Es, I1,I, X, Xs).
所以......它变得更快了吗?
?- numlist(1,100000,Zs), time(list_nth0_item_replaced(Zs,99999,x,Xs)). % 14,499,855 inferences, 0.893 CPU in 0.893 seconds (100% CPU, 16237725 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...], Xs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 7 inferences, 0.000 CPU in 0.000 seconds (99% CPU, 18377 Lips) false. ?- numlist(1,100000,Zs), time(list_nth0_item_replaced__NEW(Zs,99999,x,Xs)). % 499,996 inferences, 0.049 CPU in 0.049 seconds (100% CPU, 10158710 Lips) Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...], Xs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ; % 6 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 213988 Lips) false.
好的, 更快。但它仍然是多才多艺的吗?
?- list_nth0_item_replaced__NEW([a,b,c,d], 1, z, Xs). Xs = [a,z,c,d] ; false. ?- list_nth0_item_replaced__NEW(Xs, 1, z, [a,z,c,d]). Xs = [a,_A,c,d] ; false. ?- list_nth0_item_replaced__NEW(Es, N, X, [a,z,c,d]). N = 0, X = a, Es = [_A, z, c, d], ; N = 1, X = z, Es = [ a,_A, c, d] ; N = 2, X = c, Es = [ a, z,_A, d], ; N = 3, X = d, Es = [ a, z, c,_A] ; false. ?- list_nth0_item_replaced__NEW([a,b,c,d], N, X, Xs). N = 0, Xs = [X,b,c,d] ; N = 1, Xs = [a,X,c,d] ; N = 2, Xs = [a,b,X,d] ; N = 3, Xs = [a,b,c,X] ; false.
对我来说没问题!
答案 7 :(得分:1)
像这样直截了当地做这件事怎么样?
:- use_module(library(clpfd)). list_nth0_item_replaced([_|Xs], 0, E, [E|Xs]). list_nth0_item_replaced([X|Xs], N, E, [X|Ys]) :- N #> 0, N #= N0+1, list_nth0_item_replaced(Xs, N0, E, Ys).
以下是OP指定的用例:
?- list_nth0_item_replaced([a,b,c,d],1,z,Ls).
Ls = [a,z,c,d]
; false.
上面的代码是纯粹的,所以我们可以提出更多一般性的查询 - 并期望逻辑上合理的答案:
?- list_nth0_item_replaced([a,b,c,d], N, X, Ls).
N = 0, Ls = [X,b,c,d]
; N = 1, Ls = [a,X,c,d]
; N = 2, Ls = [a,b,X,d]
; N = 3, Ls = [a,b,c,X]
; false.