The question is to find the last but one character in a list, e.g.
?- last_but_one(X, [a,b,c,d]).
X = c.
My code is:
last
Here are more ways how you could do it. I wouldn't recommend actually using any of the following methods, but IMO they are interesting as they give a different view on the other codes and on the Prolog library provided by the respective Prolog processors:
In the first three variants, we delegate the "recursive part" to built-in / library predicates:
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|_]). % (SICStus only)
Alternatively, we could use vanilla home-brewed myappend/3
and 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), % for universal termination in mode (-,+)
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|_]).
Let's run the experiments1!
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))
).
Here are the runtimes2 using SICStus Prolog and SWI-Prolog3,4:
SICStus | SICStus | SWI | 4.3.2 | 4.3.3 | 7.3.20 | -------------------+---------+--------| you 0.26s | 0.10s | 0.83s |3.1×8.3× they 0.27s | 0.12s | 1.03s |3.8×8.5× f1 0.04s | 0.02s | 0.43s |10.8×21.5× f2 0.02s | 0.02s | 0.37s |18.5×18.5× mat 0.26s | 0.11s | 1.02s |3.9×9.0× dcg 1.06s | 0.77s | 1.47s |1.3×1.9× dcgx 0.31s | 0.17s | 0.97s |3.1×5.7× ap 0.23s | 0.11s | 0.42s |1.8×3.8× append 1.50s | 1.13s | 1.57s |1.0×1.3× reverse 0.36s | 0.32s | 1.02s |2.8×3.1× rev 0.04s | 0.04s | --"-- |25.6×25.6× myappend 0.48s | 0.33s | 1.56s |3.2×4.7× myreverse 0.27s | 0.26s | 1.11s |4.1×4.2×
Very impressive! In the SICStus/SWI speedup column, differences > 10% got bold-faced.
Footnote 1: All measurements shown in this answer were obtained on an Intel
Haswell processor
Core i7-4700MQ.
Footnote 2: rev/2
is offered by SICStus—but not by SWI. We compare the fastest "reverse" library predicate.
Footnote 3: The SWI command-line option -G1G
was required to prevent Out of global stack
errors.
Footnote 4: Also, the SWI command-line option -O
(optimize) was tried, but did not yield any improvement.