xMenores(_,[],[]).
xMenores(X,[H|T],[R|Z]) :-
xMenores(X,T,Z),
X > H,
R is H.
xMenores
takes three parameters:
- The first one is a number.
- The second is a list of numbers.
- The third is a list and is the variable that will contain the result.
The objective of the rule xMenores
is obtain a list with the numbers of the list (Second parameter) that are smaller than the value on the first parameter. For example:
?- xMenores(3,[1,2,3],X).
X = [1,2]. % expected result
The problem is that xMenores
returns false
when X > H
is false and my programming skills are almost null at prolog. So:
?- xMenores(4,[1,2,3],X).
X = [1,2,3]. % Perfect.
?- xMenores(2,[1,2,3],X).
false. % Wrong! "X = [1]" would be perfect.
I consider X > H, R is H.
because I need that whenever X
is bigger than H
, R
takes the value of H
. But I don't know a control structure like an if or something in Prolog to handle this.
Please, any solution? Thanks.
You could write it as a one-liner using
findall\3
:However, I suspect that the point of the exercise is to learn about recursion, so something like this would work:
It does, however, unpack the list twice on backtracking. An optimization here would be to combine the 2nd and 3rd clauses by introducing a soft cut like so:
No new code is presented in this answer.
In the following we take a detailed look at different revisions of this answer by @lurker.
Revision #1, renamed to
less_than_ver1//2
. By using dcg and clpfd, the code is both very readable and versatile:Let's query!
As a small imperfection,
less_than_ver1//2
leaves some useless choicepoints.Let's see how things went with the newer revision...
Revision #3, renamed to
less_than_ver3//2
:This code uses the if-then-else (
(->)/2
+(;)/2
) in order to improve determinism.Let's simply re-run the above queries!
Surprise! Two cases that worked before are now (somewhat) broken, and the determinism in the ground case is no better... Why?
The vanilla if-then-else often cuts too much too soon, which is particularly problematic with code which uses coroutining and/or constraints.
Note that
(*->)/2
(a.k.a. "soft-cut" orif/3
), fares only a bit better, not a lot!As
if_/3
never ever cuts more (often than) the vanilla if-then-else(->)/2
, it cannot be used in above code to improve determinism.If you want to use
if_/3
in combination with constraints, take a step back and write code that is non-dcg as the first shot.If you're lazy like me, consider using a meta-predicate like
tfilter/3
and(#>)/3
.(This is more like a comment than an answer, but too long for a comment.)
Some previous answers and comments have suggested using "if-then-else"
(->)/2
or usinglibrary(apply)
meta-predicateinclude/3
. Both methods work alright, as long as only plain-old Prolog arithmetics—is/2
,(>)/2
, and the like—are used ......, but when doing the seemingly benign switch from
(>)/2
to(#>)/2
, we lose soundness!Using
( if -> then ; else )
The control structure you might be looking for is
( if -> then ; else )
.Warning: you should probably swap the order of the first two arguments:
However, if you are writing real code, you should almost certainly go with one of the predicates in library(apply), for example
include/3
, as suggested by @CapelliC:See the implementation of
include/3
if you want to know how this kind of problems are solved. You will notice thatlessthan/3
above is nothing but a specialization of the more generalinclude/3
in library(apply):include/3
will reorder the arguments and use the( if -> then ; else )
."Declarative" solution
Alternatively, a less "procedural" and more "declarative" predicate:
(
lessthan_if/3
andlessthan_decl/3
are nearly identical to the solutions by Nicholas Carey, except for the order of arguments.)On the downside,
lessthan_decl/3
leaves behind choice points. However, it is a good starting point for a general, readable solution. We need two code transformations:<
and>=
with CLP(FD) constraints:#<
and#>=
;You will arrive at the solution by lurker.
A different approach
The most general comparison predicate in Prolog is
compare/3
. A common pattern using it is to explicitly enumerate the three possible values forOrder
:(Compared to any of the other solutions, this one would work with any terms, not just integers or arithmetic expressions.)
Replacing
compare/3
withzcompare/3
:This is definitely more code than any of the other solutions, but it does not leave behind unnecessary choice points:
In the other cases, it behaves just as the DCG solution by lurker:
Unless you need such a general approach,
include(>(X), List, Result)
is good enough.This can also be done using a DCG:
You can write your predicate as:
This answer by @Boris presented a logically pure solution which utilizes
clpfd:zcompare/3
to help improve determinism in certain (ground) cases.In this answer we will explore different ways of coding logically pure Prolog while trying to avoid the creation of useless choicepoints.
Let's get started with
zcompare/3
and(#<)/3
!zcompare/3
implements three-way comparison of finite domain variables and reifies the trichotomy into one of<
,=
, or>
.(#<)/3
for reifying the dichotomy into one oftrue
orfalse
.Consider the answers of the following queries:
Note that for all items to be selected both
Ord = (<)
andB = true
holds.Here's a side-by-side comparison of three non-dcg solutions based on clpfd:
zcompare/3
and first-argument indexing on the three cases<
,=
, and>
.(#<)/3
and first-argument indexing on the two casestrue
andfalse
.(#<)/3
in combination withif_/3
.Note that we do not need to define auxiliary predicates in the right column!
Next, let's use dcg!
if_//3
instead ofif_/3
.less_than([1,2,3],Zs,3)
vsphrase(less_than([1,2,3],3),Zs)
.The following dcg implementations correspond to above non-dcg codes:
OK! Saving the best for last... Simply use meta-predicate
tfilter/3
together with(#>)/3
!