Division two naturals in prolog [duplicate] - prolog

This question already has answers here:
Dividing two integers with an alternative way
(3 answers)
Closed 7 years ago.
I have written the following code:
nat(0).
nat(s(X)) :- nat(X).
divide(0,_,0).
divide(X,Y,D) :- X#<Y, D is 0.
divide(X,s(0),X).
divide(_,0,undefined) :- !.
Everything is right up to here. but what should i write, to calculate the division of two other naturals? for example
divide(s(s(s(s(s(s(s(s(0)))))))),s(s(0)),D).???

I think the easiest way would be to define subtraction and then apply it recursively with a counter. I'm not able to run this right now but I assume it'd look something like this:
nat(0).
nat(s(X)) :- nat(X).
divide(_,0,_):- !, fail.
divide(X,Y,R):-
iter_subtract(X,Y,0,R).
iter_subtract(X,Y,N,N):- X#<Y, !.
iter_subtract(X,Y,C,R):-
subtract(X,Y,N),
D = s(C),
iter_subtract(N,Y,D,R).
subtract(A,0,A):-!.
subtract(s(X),s(B),R):-
subtract(X,B,R).

you can easily divide two numbers as following:
mydiv(0,_,0).
mydiv(_,0,undefined):- !.
mydiv(X,Y,D) :- X >= Y , Z is X - Y , mydiv(Z,Y,M) , D is M + 1 .
output:
?- mydiv(10,4,Q). false.
?- mydiv(100,2,Q). Q = 50 .
to run with debug info:
mydiv(0,_,0) :- write('mydiv(0,_,0)\n').
mydiv(_,0,undefined):- write('mydiv(_,0,undefined)\n'),!.
mydiv(X,Y,D) :- write('mydiv('),write(X) , write(','), write(Y) , write(',') , write(D) , write(')') , nl,
X >= Y , Z is X - Y , mydiv(Z,Y,M) , D is M + 1 .
output:
?- mydiv(20,2,Q). mydiv(20,2,_G3941) mydiv(18,2,_L1420)
mydiv(16,2,_L1435) mydiv(14,2,_L1450) mydiv(12,2,_L1465)
mydiv(10,2,_L1480) mydiv(8,2,_L1495) mydiv(6,2,_L1510)
mydiv(4,2,_L1525) mydiv(2,2,_L1540) mydiv(0,__,0) Q = 10 .
with natural function:
natural(0).
natural(X) :- X < 0 , !, fail.
natural(X) :- Y is X - 1 , natural(Y).
mydiv(0,_,0) :- write('mydiv(0,_,0)\n').
mydiv(_,0,undefined):- write('mydiv(_,0,undefined)\n'),!.
mydiv(X,Y,D) :- write('mydiv('),write(X) , write(','), write(Y) , write(',') , write(D) , write(')') , nl,
natural(X), natural(Y), X >= Y , Z is X - Y , mydiv(Z,Y,M) , D is M + 1 .

Related

SWI-Prolog predicates member and nth1 without unification?

If I use the predicates member or the nt1 in SWI-Prolog as follows:
?- member(X, [A]).
X = A.
or
nth1(N, [A], X).
N = 1,
A = X.
The interpreter unifies the variable A as X.
Is their some alternative version of this functions which does not use the unification. Means, if I call something like this:
?- _member(X, [A]).
it would give
false
as long as the call is not
?- member(X, [X]).
which would lead to
true
And in the same way
_nth1(N, [A], X).
would give false
but
_nth1(N, [X], X).
would give
N = 1
Seems like you just need to roll your own using ==/2 instead of =/2:
See https://swish.swi-prolog.org/p/DqhYGuEf.pl
is_member_of( X , [Y|_] ) :- X == Y .
is_member_of( X , [_|Ys] ) :- is_member_of(X,Ys) .
is_nth0( N , L , E ) :- is_nth(0,L,E,N) .
is_nth1( N , L , E ) :- is_nth(1,L,E,N) .
is_nth( N , [Y|_] , X , N ) :- X == Y .
is_nth( I , [_|Ys] , X , N ) :- J is I+1, is_nth(J,Ys,X,N) .

Why my predicate div/4 enters an infinite loop after giving me one answer?

I'm trying to implement the Euclidean division (a = bq + r) using Peano Axioms, so far I've done this predicates:
% Dd = Dr * C + R
div(Dd,s(0),Dd,0).
div(Dd,Dr,C,R) :-
less(Dr,Dd),
mod(Dd,Dr,R),
times(Dr,C,X),
plus(X,R,Dd).
mod(X,Y,X) :-
less(X,Y).
mod(X,Y,Z) :-
plus(X1,Y,X),
mod(X1,Y,Z).
less(0,s(X)) :-
nat(X).
less(s(X),s(Y)) :-
less(X,Y).
times(0,_,0).
times(s(0),X,X):-
nat(X).
times(s(X),Y,Z) :-
times(X,Y,M),
plus(Y,M,Z).
plus(0,Y,Y) :-
nat(Y).
plus(s(X),Y,s(Z)) :-
plus(X,Y,Z).
nat(0).
nat(s(X)):-
nat(X).
Every predicate seems to work perfectly, the one problem I'm having is that doing:
?- div(s(s(s(s(0)))), s(s(0)), X, Y).
Gives me the correct answer, and then instead of saying there are no more answers enters an infinite loop.
I don't understand why div/4 do not stop, any idea why?
Thanks in advance
If you keep adding test cases you will find your problem. One of the predicates is wrong. Since I don't know how your method works because it is not like I was expecting based on the Wikipedia article I can make no more progress. Please answer the questions in the comment.
Create unit tests for each predicate. (which are below).
Create the new predicate peano_number/1 as a proper type check by using SWI-Prolog has_type/2 because nat/1 would allow nat(N) where N is a variable, and that should not pass as a type check. A variable is not a Peano number.
Added more is_of_type(peano_number,X) as checks on input values, but also as checks on results of predicate. Once the code is working correctly, these can be removed and if the code is valid, all the unit test should still pass.
Working on the rest.
:- multifile
error:has_type/2.
error:has_type(peano_number,Peano_number) :-
peano_number(Peano_number).
div(Dd,s(0),Dd,0).
div(Dd,Dr,C,R) :-
less(Dr,Dd),
mod(Dd,Dr,R),
times(Dr,C,X),
plus(X,R,Dd).
mod(X,Y,X) :-
less(X,Y).
mod(X,Y,Z) :-
plus(X1,Y,X),
mod(X1,Y,Z).
less(0,s(X)) :-
is_of_type(peano_number,X).
less(s(X),s(Y)) :-
less(X,Y).
% Cuts added to make predicates determinate
times(0,_,0) :- !.
times(s(0),X,X) :-
!,
is_of_type(peano_number,X).
times(s(X),Y,Z) :-
is_of_type(peano_number,X),
is_of_type(peano_number,Y),
times(X,Y,M),
plus(Y,M,Z),
is_of_type(peano_number,Z).
plus(0,Y,Y) :-
is_of_type(peano_number,Y).
plus(s(X),Y,s(Z)) :-
is_of_type(peano_number,X),
is_of_type(peano_number,Y),
plus(X,Y,Z),
is_of_type(peano_number,Z).
nat(0).
nat(s(X)):-
nat(X).
% Added peano_number(N) because
% ?- nat(X).
% X = 0 ;
% X = s(0) ;
% X = s(s(0)) ;
% ...
%
% while valid is not useful for type checking.
%
% A Peano number can not be a variable.
%
% ?- peano_number(X).
% false.
peano_number(N) :-
ground(N),
N = 0.
peano_number(s(X)):-
ground(X),
peano_number(X).
:- begin_tests(peano_axioms).
nat_test_case_generator(success ,0 ).
nat_test_case_generator(success ,s(0) ).
nat_test_case_generator(success ,s(s(0)) ).
nat_test_case_generator(fail ,-1 ).
nat_test_case_generator(fail ,1 ).
nat_test_case_generator(fail ,a ).
nat_test_case_generator(fail ,s(1) ).
test('nat success',[forall(nat_test_case_generator(success,X))]) :-
nat(X).
test('nat fail',[fail,forall(nat_test_case_generator(fail,X))]) :-
nat(X).
peano_number_test_case_generator(success ,0 ).
peano_number_test_case_generator(success ,s(0) ).
peano_number_test_case_generator(success ,s(s(0)) ).
peano_number_test_case_generator(fail ,-1 ).
peano_number_test_case_generator(fail ,1 ).
peano_number_test_case_generator(fail ,a ).
peano_number_test_case_generator(fail ,s(1) ).
peano_number_test_case_generator(fail ,_ ).
test('peano_number success',[forall(nat_test_case_generator(success,X))]) :-
is_of_type(peano_number,X).
test('peano_number fail',[fail,forall(nat_test_case_generator(fail,X))]) :-
is_of_type(peano_number,X).
plus_test_case_generator(01, success, 0 , 0 , 0 ). % 0 + 0 = 0
plus_test_case_generator(02, success, 0 , s(0), s(0) ). % 0 + 1 = 1
plus_test_case_generator(03, success, 0 , s(s(0)), s(s(0)) ). % 0 + 2 = 2
plus_test_case_generator(04, success, s(0), 0 , s(0) ). % 1 + 0 = 1
plus_test_case_generator(05, success, s(0), s(0), s(s(0)) ). % 1 + 1 = 2
plus_test_case_generator(06, success, s(0), s(s(0)), s(s(s(0))) ). % 1 + 2 = 3
plus_test_case_generator(07, success, s(s(0)), 0 , s(s(0)) ). % 2 + 0 = 2
plus_test_case_generator(08, success, s(s(0)), s(0), s(s(s(0))) ). % 2 + 1 = 3
plus_test_case_generator(09, success, s(s(0)), s(s(0)), s(s(s(s(0)))) ). % 2 + 2 = 4
plus_test_case_generator(10, fail , s(1), s(s(0)), s(s(s(s(0)))) ). % s(1) is not a valid Peano number
plus_test_case_generator(11, fail , s(s(0)), a, s(s(s(s(0)))) ). % a is not a valid Peano number
plus_test_case_generator(12, fail , s(s(0)), s(s(0)), 3 ). % 3 is not a valid Peano number
plus_test_case_generator(13, fail , s(s(0)), s(s(0)), s(s(s(0))) ). % 2 + 2 does not equal 3
test('plus success',[forall(plus_test_case_generator(_,success,A,B,C))]) :-
plus(A,B,C).
test('plus fail',[fail,forall(plus_test_case_generator(_,fail,A,B,C))]) :-
plus(A,B,C).
times_test_case_generator(01, success, 0 , 0 , 0 ). % 0 * 0 = 0
times_test_case_generator(02, success, 0 , s(0), 0 ). % 0 * 1 = 0
times_test_case_generator(03, success, 0 , s(s(0)), 0 ). % 0 * 2 = 0
times_test_case_generator(04, success, s(0), 0 , 0 ). % 1 * 0 = 0
times_test_case_generator(05, success, s(0), s(0), s(0) ). % 1 * 1 = 1
times_test_case_generator(06, success, s(0), s(s(0)), s(s(0)) ). % 1 * 2 = 2
times_test_case_generator(07, success, s(s(0)), 0 , 0 ). % 2 * 0 = 0
times_test_case_generator(08, success, s(s(0)), s(0), s(s(0)) ). % 2 * 1 = 2
times_test_case_generator(09, success, s(s(0)), s(s(0)), s(s(s(s(0)))) ). % 2 * 2 = 4
times_test_case_generator(10, fail , s(1), s(s(0)), s(s(s(s(0)))) ). % s(1) is not a valid Peano number
times_test_case_generator(11, fail , s(s(0)), a, s(s(s(s(0)))) ). % a is not a valid Peano number
times_test_case_generator(12, fail , s(s(0)), s(s(0)), 3 ). % 3 is not a valid Peano number
times_test_case_generator(13, fail , s(s(0)), s(s(0)), s(s(s(0))) ). % 2 * 2 does not equal 3
test('times success',[forall(times_test_case_generator(_,success,A,B,C))]) :-
times(A,B,C).
:- end_tests(peano_axioms).
I can't quite get my head around this code. If I understand correctly, it tries to find a divisor and a remainder by trying successively larger divisors. Because the div/4 just states the properties of divisor and remainder i.e. it tests whether anything found is appropriate. The generate part is buried in times/3 which generates possible C for Dr*C=X.
I found this:
I don't know what mod/3 does.
times/3 has a problem because there is ambiguity on two clauses
nat/1 is not a test, but a generator of natural numbers
the infinite loop comes from the fact that div/4 does not know that if it has found one solution, there are no more solutions. If will try successively larger divisor. We can tell it to stop at the far end of the clause, where a solution has been found, via a cut.
One must pay
This leads to:
div(Dd,s(0),Dd,0).
div(Dd,Dr,C,R) :-
% just a guard
less(Dr,Dd),
% Dr known, C,X free,
% --> C should be incremented by 1 on backtracking, and X computed
% --> and we will stop latest when X > Dd
times(Dr,C,X),
% --> Dd known, X has been generated by the above, R is being computed
plus(X,R,Dd),
less(R,Dr),
% solution found at this point
!.
less(0,s(_)).
less(s(X),s(Y)) :-
less(X,Y).
times(0,_,0).
times(s(X),Y,Z) :-
times(X,Y,M),
plus(Y,M,Z).
plus(0,Y,Y) :-
nat(Y).
plus(s(X),Y,s(Z)) :-
plus(X,Y,Z).
nat(0).
nat(s(X)):-
nat(X).
Addendum
As said earlier it is easier to replace Peano recursive definitions by the easier to-read list of ... aynthing really. In this case, the anonymous variable _. If Peano had known about lists, he would probably have used these instead (same as for Gödel, who would probably have used proper arrays instead of heroic Gödel encoding)
For example for times/3 (note the disambiguation of [_|Rs], which aliases to [_] and [_,_|Rs], into two distinct cases [_] and [_,_|Rs]; I was stumped by this for some time) and plus/3:
times([],X,[]) :- nat(X).
times([_],X,X) :- nat(X).
times([_,S|Rs],Y,Z) :- times([S|Rs],Y,M),plus(Y,M,Z).
% adding two lists
plus([],Y,Y) :- nat(Y).
plus([_|X],Y,[_|Z]) :- plus(X,Y,Z).
% a list of any length is equivalent to a natural number
% this predicate not only tests, but also constructs natural
% numbers; try "nat(X)"
nat([]).
nat([_|X]) :- nat(X).
The above can be tested. The tests for plus/3 work well:
:- begin_tests(plus).
% 0 variables (thus a test)
test('0+0=0') :- plus([],[],[]).
test('1+1=2') :- plus([_],[_],[_,_]).
test('2+2=4') :- plus([_,_],[_,_],[_,_,_,_]).
% 1 variable
test('x+1=2') :- bagof(X,plus(X,[_],[_,_]),L),L=[[_]].
test('1+x=2') :- bagof(X,plus([_],X,[_,_]),L),L=[[_]].
test('1+1=x') :- bagof(X,plus([_],[_],X),L),L=[[_,_]].
test('3+x=5') :- bagof(X,plus([_,_,_],X,[_,_,_,_,_]),L),L=[[_,_]].
test('x+3=5') :- bagof(X,plus(X,[_,_,_],[_,_,_,_,_]),L),L=[[_,_]].
% 2 variables
test('x+y=3') :- bagof(sol(X,Y),plus(X,Y,[_,_,_]),L), L= [sol([], [_,_,_]), sol([_], [_, _]), sol([_, _], [_]), sol([_, _, _], [])].
% test('3+x=y') :- ... plus([_,_,_],X,Y) generates infinitely many solutions; good; how to run this 5 times for example??
% test('x+3=y') :- ... plus([_,_,_],X,Y) generates infinitely many solutions; good; how to run this 5 times for example??
% 3 variables (goes to infinity)
% we cannot test plus(X,Y,Z); the coolest would be for plus/3 to then generate
% all possible X,Y,Z triplets with increasing Z for example. How to do that?
:- end_tests(plus).
?- run_tests(plus).
% PL-Unit: plus ......... done
% All 9 tests passed
true.
But the tests for times/3 go into infinity after the first successful result. I'm not happy about that.
:- begin_tests(times).
% 0 variables
test('0*0=0') :- times([],[],[]).
test('0*2=0') :- times([],[_,_],[]).
test('2*0=0') :- times([_,_],[],[]).
test('1*1=1') :- times([_],[_],[_]).
test('1*2=1') :- times([_],[_,_],[_]).
test('2*1=1') :- times([_,_],[_],[_]).
% element 0 does not work
% test('0*x=0') :- generates all naturals
% test('x*0=0') :- generates all naturals
% test('x*y=0') :- generates all pairs of naturals
test('1*x=1') :- bagof(X,times([_],X,[_]),L),L=[[_]].
test('2*x=4') :- bagof(X,times([_,_],X,[_,_,_,_]),L),L=[[_,_]]. % blows stack
test('3*x=4',[fail]) :- times([_,_,_],_X,[_,_,_,_]).
test('x*1=1') :- bagof(X,times(X,[],[]),L),L=[[_]].
test('x*2=4') :- bagof(X,times(X,[_,_],[_,_,_,_]),L),L=[[_,_]].
test('1*3=x') :- bagof(X,times(X,[],[]),L),L=[[_]].
test('x*2=x') :- bagof(X,times(X,[_,_],[_,_,_,_]),L),L=[[_,_]].
:- end_tests(times).
?- run_tests(times).
% PL-Unit: times .......^C
Interrupted test times:'2*x=4'

Even sum & Odd product of a list, Swi-Prolog [duplicate]

I have a list of numbers, I need to calculate the sum of the even numbers of the list and the product of the odd numbers of the same list. I'm new in Prolog, and my searches so far weren't successful. Can anyone help me solve it ?
l_odd_even([]).
l_odd_even([H|T], Odd, [H|Etail]) :-
H rem 2 =:=0,
split(T, Odd, Etail).
l_odd_even([H|T], [H|Otail], Even) :-
H rem 2 =:=1,
split(T, Otail, Even).
Here is a suggestion for the sum of the even numbers from a list:
even(X) :-
Y is mod(X,2), % using "is" to evaluate to number
Y =:= 0.
odd(X) :- % using even
Y is X + 1,
even(Y).
sum_even(0, []). % empty list has zero sum
sum_even(X, [H|T]) :-
even(H),
sum_even(Y, T),
X is Y+H.
sum_even(X, [H|T]) :-
odd(H),
sum_even(X, T). % ignore the odd numbers
Note: My Prolog has oxidized, so there might be better solutions. :-)
Note: Holy cow! There seems to be no Prolog support for syntax highlighting (see here), so I used Erlang syntax.
Ha, it really works. :-)
Running some queries in GNU Prolog, I get:
| ?- sum_even(X,[]).
X = 0 ?
yes
| ?- sum_even(X,[2]).
X = 2 ?
yes
| ?- sum_even(X,[3]).
X = 0 ?
yes
| ?- sum_even(X,[5,4,3,2,1,0]).
X = 6 ?
yes
The ideas applied here should enable you to come up with the needed product.
Use clpfd!
:- use_module(library(clpfd)).
Building on meta-predicate foldl/4, we only need to define what a single folding step is:
sumprod_(Z,S0,S) :-
M #= Z mod 2,
rem_sumprod_(M,Z,S0,S).
rem_sumprod_(0,Z,S0-P,S-P) :-
S0 + Z #= S.
rem_sumprod_(1,Z,S-P0,S-P) :-
P0 * Z #= P.
Let's fold sumprod_/3 over the list!
l_odd_even(Zs,ProductOfOdds,SumOfEvens) :-
foldl(sumprod_,Zs,0-1,SumOfEvens-ProductOfOdds).
Sample query:
?- l_odd_even([1,2,3,4,5,6,7],Odd,Even).
Odd = 105,
Even = 12.
Alternatively, we can define sumprod_/3 even more concisely by using if_/3 and zeven_t/3:
sumprod_(Z,S0-P0,S-P) :-
if_(zeven_t(Z), (S0+Z #= S, P0=P),
(P0*Z #= P, S0=S)).
untested!
sum_odd_product_even([], S, P, S, P).
sum_odd_product_even([H|T], S0, P0, S, P) :-
S1 is S0 + H,
sum_even_product_odd(T, S1, P0, S, P).
sum_even_product_odd([], S, P, S, P).
sum_even_product_odd([H|T], S0, P0, S, P) :-
P1 is P0 * H,
sum_odd_product_even(T, S0, P1, S, P).
sum_odd_product_even(L, S, P) :-
sum_odd_product_even(L, 0, 1, S, P).
sum_even_product_odd(L, S, P) :-
sum_even_product_odd(L, 0, 1, S, P).
It shouldn't get much simpler than
%
% invoke the worker predicate with the accumulators seeded appropriately.
%
odds_and_evens( [O] , P , S ) :- odds_and_evens( [] , O , 0 , P , S ) .
odds_and_evens( [O,E|Ns] , P , S ) :- odds_and_evens( Ns , O , E , P , S ) .
odds_and_evens( [] , P , S , P , S ) . % if the list is exhausted, we're done.
odds_and_evens( [O] , X , X , P , S ) :- % if it's a single element list, we've only an odd element...
P is X*O , % - compute it's product
. % - and we're done.
odds_and_evens( [O,E|Ns] , X , Y , P , S ) :- % if the list is at least two elements in length'e both an odd and an even:
X1 is X*O , % - increment the odd accumulator
Y1 is Y+E , % - increment the even accumulator
odds_and_evens( Ns , X1 , Y1 , P , S ) % - recurse down (until it coalesces into one of the two special cases)
. % Easy!

How to write a predicate minmax(L, X, Y)?

How to write a predicate minmax(L, X, Y) to find out min value of X and max value of Y in list of integer L.
Example:
?- minmax([1, -10, 1, 0, 7, 7], X, Y).
X = -10, Y = 7.
Let's define list_minnum_maxnum/3 like list_minnum/2:
list_minnum_maxnum([E|Es],Min,Max) :-
V is E,
list_minnum0_minnum_maxnum0_maxnum(Es,V,Min,V,Max).
list_minnum0_minnum_maxnum0_maxnum([] ,Min ,Min,Max ,Max).
list_minnum0_minnum_maxnum0_maxnum([E|Es],Min0,Min,Max0,Max) :-
V is E,
Min1 is min(Min0,V),
Max1 is max(Max0,V),
list_minnum0_minnum_maxnum0_maxnum(Es,Min1,Min,Max1,Max).
Sample query as given by the OP:
?- list_minnum_maxnum([1,-10,1,0,7,7], Min,Max).
Min = -10,
Max = 7.
Note that this implementation of list_minnum_maxnum/3 works with all kinds of numbers.
?- list_minnum_maxnum([1,-10,1,0,7.2,7,7], Min,Max).
Min = -10,
Max = 7.2.
If you only care about handling integers, use clpfd!
:- use_module(library(clpfd)).
We define list_zmin_zmax/3 as follows:
list_zmin_zmax([E|Es],Min,Max) :-
V #= E,
list_zmin0_zmin_zmax0_zmax(Es,V,Min,V,Max).
list_zmin0_zmin_zmax0_zmax([] ,Min ,Min,Max ,Max).
list_zmin0_zmin_zmax0_zmax([E|Es],Min0,Min,Max0,Max) :-
V #= E,
Min1 #= min(Min0,V),
Max1 #= max(Max0,V),
list_zmin0_zmin_zmax0_zmax(Es,Min1,Min,Max1,Max).
Same sample use as before:
?- list_zmin_zmax([1,-10,1,0,7,7], Min,Max).
Min = -10,
Max = 7.
OK! What about support for non-integer numbers?
?- list_zmin_zmax([1,-10,1,0,7.2,7,7], Min,Max).
ERROR: Domain error: `clpfd_expression' expected, found `7.2'
We expected getting an error, we got an error...
Note that thanks to clpfd, we can run more general queries, too!
?- list_zmin_zmax([A,B], Min,Max).
A #>= Min, Max #>= A, Min #= min(A,B),
B #>= Min, Max #>= B, Max #= max(A,B).
As noted, you need to iterate over the list, accumulating the min and max values as you go. So, assuming that you have to write this from scratch, the first thing you need to do is decompose the problem into simple steps:
You need a means of comparing two objects and determine which is the lower or higher.
You need a means of iterating over the loop and tracking the min and max values seen as you go.
That leads to a min/3 and max/3, thus:
min(X,X,X).
min(X,Y,X) :- X < Y .
min(X,Y,Y) :- X > Y .
max(X,X,X).
max(X,Y,X) :- X > Y .
max(X,Y,Y) :- X < Y .
For your purposes here, one could even combine them into a single predicate, if you liked:
rank( X , X , X , X ) .
rank( X , Y , X , Y ) :- X < Y .
rank( X , Y , Y , X ) :- X > Y .
A pretty typical programming pattern in Prolog is to have a simple public API predicate that invokes a private "worker" predicate that does the actual work. Often the worker predicate will carry temporary "accumulator" variables that simplify the job. Your public predicate might look like:
minmax([X|Xs],Min,Max) :- minmax_scan( Xs , X , X , Min , Max ).
Here, your public API predicate accepts a non-empty list, seeding the min/max accumulators the worker predicate uses with the head of the list, then calling the worker predicate with the tail of the list.
Your worker predicate then might look like this:
% if the list is empty, we've solved the puzzle, right?
minmax_scan( [] , Min , Max , Min , Max ) .
% if the list is non-empty, we need to compare its head to
% the current value for min/max to determine the new values for min/max
% (which might be the same), and then recurse down on the tail of the list
minmax_scan( [X|Xs] , CurrMin , CurrMax , Min , Max ) :-
min( X , CurrMin , NextMin ) ,
max( X , CurrMax , NextMax ) ,
minmax_scan( Xs , NextMin , NextMax , Min , Max )
.
Easy!
Here's something convoluted and a bit complex.
is_minmax(A,B-D,C-E) :-
D is min(...),
E is max(...) .
pair(A,B,A-B).
minmax(L,MIN,MAX) :-
L=[A|_], length(L,N), N2 is N-1,
length(L2,N2), append(L2,[MIN],L22),
length(L3,N2), append(L3,[MAX],L33),
maplist(pair, [A|L2], L22, KL2),
maplist(pair, [A|L3], L33, KL3),
maplist(is_minmax, L, KL2, KL3).
(works in SWI Prolog). Try to figure out what to write in place of dots ....
take the first value from the list, then examine each other element of the list, selecting lower/higher values as temporary min/max.
When at the end of list, you have both...
minmax([First|Rest], Min, Max) :-
minmax(Rest, First, First, Min, Max).
minmax([], Min, Max, Min, Max).
minmax([Value|Ns], MinCurr, MaxCurr, Min, Max) :-
....
minmax(Ns, MinNext, MaxNext, Min, Max).
I'll let you write the tests before the recursive call (i.e. fill the dots!)
edit just to point out library(aggregate), available in several Prolog systems:
1 ?- [user].
minmax(L, X, Y) :- aggregate( (min(E), max(E)), member(E, L), (X, Y) ).
|:
true.
2 ?- minmax([1, -10, 1, 0, 7, 7], X, Y).
X = -10,
Y = 7.

Retrieving all the numbers from a given interval in Prolog

I am new into the world of Prolog, and I would like to write a rule that return all the elements in a specific range.
I intend to do something like
Ex:
foo(X, Low, High) :- X > Low, X < High.
And when I type foo(X, 2, 5), it should return 3, and then 4.
It seems that my approach is wrong, and I would like to know which is the correct way to do it.
When written like that, Prolog doesn't know what kind of numbers do you want (and whether you even want numbers).
One way to implement this would be:
range(X, L, H) :- X is L + 1, X < H.
range(X, L, H) :- L1 is L + 1, L1 < H, range(X, L1, H).
the easy answer: between/3:
?- between(3,4,X).
X = 3 ;
X = 4.
implementing the exact behaviour is kinda trivial this way.
the reason that your approach doesn't work is the definition of </2: both arguments should be instantiated. so, if you want to implement it without using between/3 you should do something like svick's suggestion.
Using SWI-Prolog and library(clpfd), you can write
:- use_module(library(clpfd)).
foo(X,Low,High) :-
X #> Low,
X #< High,
label([X]).
You could also do this (pretty much a reimplementatio of between/3:
range( X , Y , Z ) :-
integer(X) ,
integer(Y) ,
range1(X,Y,Z)
.
range1( X , X , X ) . % X equals Y
range1( X , Y , X ) :- X < Y .
range1( X , Y , Z ) :- X < Y , X1 is X+1 , range( X1 , Y , Z ) .
range1( X , Y , X ) :- X > Y .
range1( X , Y , Z ) :- X > Y , X1 is X-1 , range( X1 , Y , Z ) .

Resources