How can I pass a predicate as parameter for another predicate in Prolog?
Asked Answered
S

3

5

I have these 3 predicates:

times(X, Y):-
    Result is X * Y.
minus(X, Y):-
    Result is X - Y.
plus(X, Y):-
    Result is X + Y.

and I want to pass for example times(2,2) in the plus(X, Y) like this plus(times(2,2), minus(X, Y)).

Stupefy answered 19/10, 2014 at 17:54 Comment(2)
please read about metacallsUnderpinnings
Based on what @Underpinnings wrote above, in SWI-prolog you can use call to call an existing predicate, for example a variable containing this predicate, and pass it on a list of arguments. Very useful for creating Prolog generics! You can use call(Operator, ListOfArguments) or call(Operator, Arg1, Arg2) to create functions that do things, return results, but you don't know or care to know what exactly happens as long as the result is of a certain type, for example +(1,Output),call(Arithmetic(Input1,Input2,Output). Perform some function/3 that returns a number, add 1 to the result.Sensible
T
5

The relationship between the title of your question and the text of your question is unclear to me, and I think @false is probably right that there is a more fundamental misunderstanding about Prolog here. I don't know if this really addresses your need or not, but the alternative here is writing your own evaluator.

eval(times(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult * YResult.
eval(minus(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult - YResult.
eval(plus(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult + YResult.

The recursive calls to eval/2 inside the bodies of each of these rules are needed to handle cases like plus(times(2,2), minus(X, Y)). Then you need a rule for numbers:

eval(Num, Num) :- number(Num).

This works great for cases like this:

?- eval(plus(times(2,2), minus(7,1)), Result).
Result = 10.

It doesn't do you any good for cases like this:

?- eval(plus(times(2,2), minus(X,Y)), Result).
ERROR: Out of local stack

Of course, it would work if we established bindings for X and Y before getting there, but if you want it to generate possible solutions for X and Y you're out of luck, you'll need to use clpfd. The reason for this curious error, if you trace in, is because number(X) when X is unbound is false, so it is actually generating new clauses involving the times, minus and plus structures and trying them, which isn't what you want in an evaluator.

Edit: implementing printterm/1.

The eval/2 predicate shows you how to perform a recursive tree walk. The principal is the same with making a pretty printer. I am very lazy so I will only sketch it, you'll have to fill in the details yourself.

printterm(T) :- format_term(T, Formatted), write(Formatted), nl.

format_term(plus(X,Y), Formatted) :- 
  format_term(X, XFormatted),
  format_term(Y, YFormatted),
  format(atom(Formatted), '(~a + ~a)', [XFormatted, YFormatted]).

% other format_term clauses here for other arithmetic expressions

format_term(X, X) :- number(X).

Hope this helps!

Taker answered 19/10, 2014 at 20:16 Comment(1)
Thx, you got an idé for a printterm/1 predicate that would for a command like this ?- printterm( plus(minus(8,2), times(4,-3))). Print out: ((8 - 2) + (4 * -3))Stupefy
W
4

First, you need to understand what Prolog predicates actually describe: They are not functions, but rather relations between values. So if you want to have a predicate for addition, this needs to be a predicate with three arguments: plus(A, B, Sum). Thus in Prolog, results do not appear for free as in many other languages.

Instead of

plus(X, Y):-
   Result is X + Y.

you need to write

plus(X, Y, Result) :-
   Result is X + Y.

What you want next, is to pass intermediary values further on. In languages that support functions, this is a very easy task. But in Prolog, function symbols are uninterpreted. So either you encode everything in relations, or otherwise you implement your own version of (is)/2. For a beginner, rather stick to the first option. Thus, instead of

..., plus(times(2,2), minus(X, Y)) ...

now write

..., times(2, 2, R), plus(R, minus(X, Y), S), ...

with S being the final result.

Observe that if you encode expressions directly in relations, you have to introduce intermediary variables like R above.

So clearly, the (direct) relational notation is less elegant for a purpose like this one. For very precise areas, Prolog offers expressions as well, in particular (is)/2 and then in the much more general setting of library(clpfd).

However, as a beginner, it is a good idea to get accustomed to the relational notation first. And it might be an even better idea to study successor arithmetics first. Prior to using library(clpfd) and prior to using (is)/2.

Winger answered 19/10, 2014 at 18:58 Comment(0)
T
2

An alternative to Daniel's solution is to view terms such as plus(times(2,2), minus(5,3)) as expression objects supporting a set of operations, such as "evaluate" or "differentiate". This allows you to write goals such as:

| ?- plus(times(2,2), minus(5,3))::evaluate(Result).
Result = 6
yes

The advantage is that the rules for all operations that apply to a specific expression, e.g. times/2 are neatly encapsulated in the corresponding object. I.e. instead of clustering the predicates by operation, they are clustered by (elementary) expression. An example along these lines, but in the context of deriving and simplifying expressions can be found here.

Tonl answered 20/10, 2014 at 21:47 Comment(2)
Holy crap symdiff is cool! ?- (x**2+2*x+1)::diff(D), D::simplify(Sim). -> D = 2*x**1*1+2*1, Sim = 2*x+2. The ability to declare _ * _ as an object in Logtalk is really amazing!Taker
Thanks. Parametric objects are very versatile. See e.g. link.springer.com/chapter/10.1007%2F978-3-642-20589-7_4Tonl

© 2022 - 2024 — McMap. All rights reserved.