Prolog Recursion in lists, last but one element

前端 未结 6 663
闹比i
闹比i 2020-12-21 02:36

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         


        
6条回答
  •  攒了一身酷
    2020-12-21 03:03

    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×
    

    Edit: Added SICStus Prolog 4.3.3 benchmarking data

    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.

提交回复
热议问题