List 在Prolog中,在第N位拆分列表的更简单方法是什么?
下面是一个谓词,用于将列表拆分为:List 在Prolog中,在第N位拆分列表的更简单方法是什么?,list,prolog,List,Prolog,下面是一个谓词,用于将列表拆分为: “前端列表”front 位置N处的元素(基于0),元素 “返回列表”back …以便原始列表L可以用以下内容重新组合: append([Front,[Element],Back],L). 代码 这真的很难读 测验 我们运行上述程序: ?- run_tests(split_list). % PL-Unit: split_list ....... [a,b,c,d,e,f,g,h,i,j,k,l] ==> [] a [b,c,d,e,f,g,h,i,j
- “前端列表”
front
- 位置N处的元素(基于0),
元素
- “返回列表”
back
L
可以用以下内容重新组合:
append([Front,[Element],Back],L).
代码
这真的很难读
测验
我们运行上述程序:
?- run_tests(split_list).
% PL-Unit: split_list .......
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [] a [b,c,d,e,f,g,h,i,j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a] b [c,d,e,f,g,h,i,j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b] c [d,e,f,g,h,i,j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c] d [e,f,g,h,i,j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c,d] e [f,g,h,i,j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c,d,e] f [g,h,i,j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c,d,e,f] g [h,i,j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c,d,e,f,g] h [i,j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c,d,e,f,g,h] i [j,k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c,d,e,f,g,h,i] j [k,l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c,d,e,f,g,h,i,j] k [l]
[a,b,c,d,e,f,g,h,i,j,k,l] ==> [a,b,c,d,e,f,g,h,i,j,k] l []
. done
% All 8 tests passed
true.
好的,这样就行了
但是:
split_list(L,N,Elem,Front,Back) :-
split_list(_{list: L, index: N, element: Elem, front: Front, back: Back}).
split_list(
_{list: [L|Lr],
index: N,
element: El,
front: [L|Front],
back: Back}) :-
N>0,!,
Nm is N-1,
split_list(
_{list: Lr,
index: Nm,
element: El,
front: Front,
back: Back}).
split_list(
_{list: [L|Lr],
index: 0,
element: L,
front: [],
back: Lr}).
更清晰?不确定。但很可能会更慢
效率高吗
如果N
已知,则它以O(N)运行。因为列表是链表,所以这是获取特定索引处元素的最有效方法
如果元素位于索引N
,则表示前面的具有长度N
,因此我们可以创建一个谓词:
split_list(List, Index, Element, Front, Back) :-
length(Front, Index),
append(Front, [Element|Back], List).
split_列表(列表、索引、元素、前面、后面):-
长度(前部、索引),
追加(前面[元素|后面],列表)。
如果已知N
,则此操作将在O(N)中运行。如果要更新元素并重新插入,可以使用nth0/4:
?-L=[a,b,c,d,e],nth0(2,L,X,Temp),nth0(2,U,替换(X),Temp)。
L=[a,b,c,d,e],
X=c,
温度=[a,b,d,e],
U=[a,b,替换为(c),d,e]。
不是一个解决方案,而是一个关于测试的注释(不适合在注释中)。这个问题非常适合使用快速检查方法进行测试
例如,使用Logtalk的QuickCheck实现(当前git版本),我们可以通过编写以下代码来定义解决方案必须遵守的属性:
property(List, N) :-
split_list(List, N, Element, Front, Back),
list::append([Front,[Element],Back], List).
然后:
| ?- {lgtunit(loader)}.
...
yes
| ?- forall(
(between(1,10,N), M is N - 1),
lgtunit::quick_check(
property(+list(integer,N),+between(integer,0,M))
)
).
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
yes
上面,我们随机测试了长度为1到10的列表
通常,当所需类型已经定义(或可以快速定义)并且实现必须遵守的属性易于定义时,QuickCheck值得考虑作为测试设置的一部分
另外,lgtunit
工具还支持快速检查,使得将上面的测试移动到测试套件中变得简单。好的,这很简单。我想这应该伴随着邪恶的笑声博士。复杂性确实是线性的。但请注意,对于N
的任何值,此解决方案都需要N*2
推理。我必须承认,我花了一些时间才理解。@DavidTonhofer:我理解,对我来说也花了一些时间。也许文档应该提示这种用法。这应该是@戴维顿霍夫:是的。在基于属性测试的Logtalk实现的特定情况下,它首先遵循Haskell的QuickCheck设计,尽管后来它获得了一些与众不同的特性(值得注意的是,Logtalk增加了对在测试中使用边缘用例的支持)。
property(List, N) :-
split_list(List, N, Element, Front, Back),
list::append([Front,[Element],Back], List).
| ?- {lgtunit(loader)}.
...
yes
| ?- forall(
(between(1,10,N), M is N - 1),
lgtunit::quick_check(
property(+list(integer,N),+between(integer,0,M))
)
).
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
% 100 random tests passed
yes