T44v1
T44v1

Reputation: 157

Comparing list elements to a predicate

I need to construct a a predicate compare_to_predicate/3. It takes in a given predicate and list of numbers and proceeds to compare every element in the list using that predicate.
The given predicates are
- is_odd
- is_even
- greater_than(X)
For example:

?- compare_to_predicate([8,13,1,500], [is_odd], X).
X = [13, 1].
?- compare_to_predicate([8,13,1,500], [greater_than, 10], X).
X = [13, 500].  

What I have come up with thus far is:

is_odd(X):- 1 is mod(X,2).
is_even(X):- 0 is mod(X,2).
greater_than(X,Y):- X<Y.
compare_to_predicate([],_,[]).
compare_to_predicate([H|Tail],Functor,[H|X]):- Term =.. [Functor,H], Term, compare_to_predicate(Tail,Functor,X).  

I have a number of issues:

1)

?- compare_to_predicate([2,10,8,300],is_even,X).

will produce

X = [2, 10, 8, 300].

but

compare_to_predicate([2,10,8,301],is_even,X).

will produce

false.

I assume it has to do with the predicate encountering a number that will not return true on is_even and then terminating the whole compare_to_predicate with a false. In that case is the solution to somehow make it ignore odd numbers instead of evaluating them? If so, how would I do that?

2)

It seems that the given predicate I pass into compare_to_predicate has to have the type List as seen in

?- compare_to_predicate([8,13,1,500], [is_odd], X). 

and

?- compare_to_predicate([8,13,1,500], [greater_than, 10], X).  

I am currently simply passing a normal predicate into the Term. I'm not quite sure on how I'm supposed to do that.
It seems that compare_to_predicate([H|Tail],[Functor],[H|X]):- Term =.. [Functor,H], Term, compare_to_predicate(Tail,[Functor],X)
did the trick here. Finally:

3)

?- compare_to_predicate([8,13,1,500], [greater_than, 10], X).

It seems I need to make compare_to_predicate able to take in predicates with different arity as shown here. Is the solution supposed to be something like this?

(Term =.. [Functor,A]; Term=.. [Functor,A,B]).  

Any help will be appreciated.

Upvotes: 1

Views: 854

Answers (1)

Daniel Lyons
Daniel Lyons

Reputation: 22803

You kind of need to decide what compare_to_predicate/3 is supposed to do with values that fail the goal. I see basically three ways this can go:

  1. It can behave as a filter, and the third argument is unified with the values in the first argument that pass.
  2. It can behave as a for-all, and the third argument is unified with the whole list if they all pass and fails otherwise (your current behavior)
  3. It can behave like an for-each that just applies the predicate to each item in the list and discards the result. There is no meaningful value for the third parameter in this case.

By the way, the term surgery you're doing with =../2 is not necessary; call/N will do the right thing if you do something like call(greater(10), 20), so you can just allow the user to call your predicate like this: compare_to_predicate([1,2,3,4], greater(2), X) and use call to build the goal.

Doing the first one is pretty straightforward:

filter([], _, []).
filter([X|Xs], P, Result) :-
    filter(Xs, P, Xs1),
    (call(P, X) -> Result = [X|Xs1] ; Result = Xs1).

Doing the second one is also pretty straightforward:

forall([], _, []).
forall([X|Xs], P, [X|Xs]) :- call(P, X), forall(Xs, P, Xs).

Doing the third one is not terribly hard:

foreach([], _).
foreach([X|Xs], G) :- once(call(G, X) ; true), foreach(Xs, G).

Upvotes: 2

Related Questions