以下是更多的做法,虽然我不建议实际使用以下任何一种方法,但我认为它们很有趣,因为它们提供了对其他代码和Prolog库的不同观点,以及各自Prolog处理器提供的Prolog库:
在前三种变体中,我们将“递归部分”委托给内置/库谓词:
last_but_one_append(X,Es) :-
append(_, [X,_], Es).
:- use_module(library(lists)).
last_but_one_reverse(X, Es) :-
reverse(Es, [_,X|_]).
last_but_one_rev(X, Es) :-
rev(Es, [_,X|_]).
或者,我们可以使用纯自制的myappend/3
和myreverse/2
:
myappend([], Bs, Bs).
myappend([A|As], Bs, [A|Cs]) :-
myappend(As, Bs, Cs).
last_but_one_myappend(X, Es) :-
myappend(_, [X,_], Es).
myreverse(Es, Fs) :-
same_length(Es, Fs),
myreverse_(Es, Fs, []).
myreverse_([], Fs, Fs).
myreverse_([E|Es], Fs, Fs0) :-
myreverse_(Es, Fs, [E|Fs0]).
last_but_one_myreverse(X, Es) :-
myreverse(Es, [_,X|_]).
我们来运行实验1吧!
bench_last :-
\+ ( length(Ls, 10000000),
member(M, [you,they,f1,f2,mat,dcg,dcgx,ap,
append,reverse,rev,
myappend,myreverse]),
write(M), write(' '),
atom_concat(last_but_one_,M,P),
\+ time(call(P,_L,Ls))
).
以下是使用SICStus Prolog和SWI-Prolog运行时的数据
2,3,4:
SICStus | SICStus | SWI |
4.3.2 | 4.3.3 | 7.3.20 |
-------------------+---------+--------|
you 0.26秒 | 0.10秒 | 0.83秒 | 3.1× 8.3×
they 0.27秒 | 0.12秒 | 1.03秒 | 3.8× 8.5×
f1 0.04秒 | 0.02秒 | 0.43秒 | 10.8× 21.5×
f2 0.02秒 | 0.02秒 | 0.37秒 | 18.5× 18.5×
mat 0.26秒 | 0.11秒 | 1.02秒 | 3.9× 9.0×
dcg 1.06秒 | 0.77秒 | 1.47秒 | 1.3× 1.9×
dcgx 0.31秒 | 0.17秒 | 0.97秒 | 3.1× 5.7×
ap 0.23秒 | 0.11秒 | 0.42秒 | 1.8× 3.8×
append 1.50秒 | 1.13秒 | 1.57秒 | 1.0× 1.3×
reverse 0.36秒 | 0.32秒 | 1.02秒 | 2.8× 3.1×
rev 0.04秒 | 0.04秒 | --"-- | 25.6× 25.6×
myappend 0.48秒 | 0.33秒 | 1.56秒 | 3.2× 4.7×
myreverse 0.27秒 | 0.26秒 | 1.11秒 | 4.1× 4.2×
编辑:添加了SICStus Prolog 4.3.3基准测试数据
非常令人印象深刻!在SICStus/SWI加速列中,差异>10%的部分被加粗。
注1:本答案中显示的所有测量值均基于IntelHaswell处理器Core i7-4700MQ。
注2:SICStus提供了rev/2
,但SWI没有。我们比较最快的“反转”库谓词。
注3:SWI命令行选项-G1G
是必需的,以防止全局堆栈溢出
错误。
注4:此外,尝试使用SWI命令行选项-O
(优化),但没有产生任何改进。
last_but_one(X, [_,Y,Z|T]) :- last_but_ont(X, [Y,Z|T]).
它强制第二个参数是至少有3个元素的列表,使约束更加精确。 :) - lurkerlast_but_one(X, [X,_]).
所以再添加一个匹配第二个参数为两个元素列表的子句是多余的。 - lurker