I am trying to write a function in Scheme and Prolog that returns first, middle and last item of a list. E.g., find([4,5,8,7,9],L), L = [4,8,9]
.
I came up with this piece of code for Scheme language, but I am new to Prolog and don't know much, so how I can get the same result in Prolog?
(define (frst L)
(car L))
(define (last L)
(if (null? (cdr L))
(car L)
(last (cdr L))))
(define (nth L x)
(if (= x 1)
(car L)
(nth (cdr L) (- x 1))))
(define (firstMidLast L)
(list (frst L)
(nth L (ceiling (/ (length L) 2)))
(last L)))
Here's another way to do it!
- The "trick" is to walk down the same list at two different speeds.
- First argument indexing keeps goals
list_first_mid_last(+,?,?,?)
deterministic.
We define list_first_mid_last/4
like this:
list_first_mid_last([E|Es],E,M,L) :-
ahead_of_mid_last([E|Es],[E|Es],M,L).
ahead_of_mid_last([],[M|_],M,M).
ahead_of_mid_last([F|Fs],Es,M,L) :-
more_ahead_of_mid_last(Fs,F,Es,M,L).
more_ahead_of_mid_last([],L,[E|_],E,L).
more_ahead_of_mid_last([F|Fs],_,Es,E,L) :-
evenmore_ahead_of_mid_last(Fs,F,Es,E,L).
evenmore_ahead_of_mid_last([],L,[E|_],E,L).
evenmore_ahead_of_mid_last([F|Fs],_,[_|Es],M,L) :-
more_ahead_of_mid_last(Fs,F,Es,M,L).
Let's run a few queries and put Prolog1 and Scheme2 results side-by-side!
% Prolog % ; Scheme
?- list_first_mid_last([1],F,M,L). % > (firstMidLast `(1))
F = M, M = L, L = 1. % (1 1 1)
%
?- list_first_mid_last([1,2],F,M,L). % > (firstMidLast `(1 2))
F = M, M = 1, L = 2. % (1 1 2)
%
?- list_first_mid_last([1,2,3],F,M,L). % > (firstMidLast `(1 2 3))
F = 1, M = 2, L = 3. % (1 2 3)
%
?- list_first_mid_last([1,2,3,4],F,M,L). % > (firstMidLast `(1 2 3 4))
F = 1, M = 2, L = 4. % (1 2 4)
%
?- list_first_mid_last([1,2,3,4,5],F,M,L). % > (firstMidLast `(1 2 3 4 5))
F = 1, M = 3, L = 5. % (1 3 5)
%
?- list_first_mid_last([1,2,3,4,5,6],F,M,L). % > (firstMidLast `(1 2 3 4 5 6))
F = 1, M = 3, L = 6. % (1 3 6)
%
?- list_first_mid_last([1,2,3,4,5,6,7],F,M,L).% > (firstMidLast `(1 2 3 4 5 6 7))
F = 1, M = 4, L = 7. % (1 4 7)
Footnote 1: Using swi-prolog version 7.3.11 (64-bit).
Footnote 2: Using the scheme interpreter SCM version 5e5 (64-bit).
Assuming that in the case that the list has even number of items you can choose one of them (in this case the first one), this procedure should work:
Edited per comment from user 'repeat' (i am not versed on Scheme)
find([First|List], [First, Middle, Last]):-
append(_, [Last], [First|List]),
length(List, Length),
NLength is Length >> 1,
nth0(NLength, [First|List], Middle).
The head of the clause instantiates the First item of the list, then append/3
takes the Last item, length/2
computes the size-1 of the list, >>/2
will divide that size by 2, and nth0/3
will get the Middle item.