It is common to use a so-called “lagged argument” to benefit from first-argument indexing:
list_min([L|Ls], Min) :-
list_min(Ls, L, Min).
list_min([], Min, Min).
list_min([L|Ls], Min0, Min) :-
Min1 is min(L, Min0),
list_min(Ls, Min1, Min).
This pattern is called a fold (from the left), and foldl/4
, which is available in recent SWI versions, lets you write this as:
list_min([L|Ls], Min) :- foldl(num_num_min, Ls, L, Min).
num_num_min(X, Y, Min) :- Min is min(X, Y).
Notice though that this cannot be used in all directions, for example:
?- list_min([A,B], 5).
is/2: Arguments are not sufficiently instantiated
If you are reasoning about integers, as seems to be the case in your example, I therefore recommend you use CLP(FD) constraints to naturally generalize the predicate. Instead of (is)/2
, simply use (#=)/2
and benefit from a more declarative solution:
:- use_module(library(clpfd)).
list_min([L|Ls], Min) :- foldl(num_num_min, Ls, L, Min).
num_num_min(X, Y, Min) :- Min #= min(X, Y).
This can be used as a true relation which works in all directions, for example:
?- list_min([A,B], 5).
yielding:
A in 5..sup,
5#=min(B, A),
B in 5..sup.