4

I'm new to Prolog and would like to define a simple predicate which calculates the result depending on which function I choose to use in the arithmetic expression.

So, this was my idea:

operation(X,Y, Op, Result):-
  Result is X Op Y.

Now, I was expecting this from Prolog:

operation(3,4,'+', X).
X = 7.

But as you can probably guess, Prolog cannot identify Op as an arithmetic operation. Does anyone have an idea how this is possible?

I could not find anything on the internet yet, even though it is rather basic, I think.

Thanks in advance!

XerXes
  • 337
  • 1
  • 16

2 Answers2

5

Although the answers by Tudor and gokhans deliver the wanted result, I think there is a more elegant solution.

Portable solution

The following will work in most Prolog implementations:

operation(X, Y, Operator, Result):-
  Goal =.. [Operator, X, Y],
  Result is Goal.

Extended but SWI-Prolog specific solution

SWI-Prolog allows the definition of custom arithmetic functions. The following code extends the above for use with such user-defined functions coming from other modules:

:- meta_predicate(operation(+,+,2,-)).

operation(X, Y, Module:Operator, Result):-
  Goal =.. [Operator, X, Y],
  Module:(Result is Goal).

Notice that support for user-defined functions is deprecated in SWI-Prolog and does not work in other Prologs that do not have this feature.

Usage examples

Some examples of using these implementations of operation/4:

?- operation(1, 2, mod, X).
X = 1.

?- operation(1, 2, //, X).
X = 0.

?- operation(1, 2, /, X).
X = 0.5.

?- operation(1, 2, -, X).
X = -1.

?- operation(1, 2, +, X).
X = 3.
Wouter Beek
  • 3,307
  • 16
  • 29
  • Wouter, you're missing a closed parenthesis. That apart, are arithmetic operators callable ? I don't think, but I could be wrong... – CapelliC Apr 10 '14 at 09:29
  • @CapelliC Thanks, I've fixed the bracket, and included some examples of the predicate in use. I'm not sure why arithmetic operators would not be callable, but I may be the one here who's getting it wrong... Could you elaborate a bit on that? – Wouter Beek Apr 10 '14 at 09:45
  • I would expect a callable succeed on *calling*. `?- X + Y.` gives error, I think. Indeed, I get `ERROR: toplevel: Undefined procedure: (+)/2` – CapelliC Apr 10 '14 at 09:47
  • @CapelliC Thanks for clarifying your point! I was under the impression that they were callable, for the following two reasons: (1) `callable(1 + 2)` succeeds, (2) `call(X is 1 + 2)` succeeds. (I'm using SWI-Prolog here.) But indeed, if you leave out the `is` it is no longer callable. What would be the best way of naming such operators that are valid arguments to meta-predicates but that must occur with `is/2` in order to be callable? – Wouter Beek Apr 10 '14 at 09:58
  • Dunno. I'd like to see more consistent support for arithmetic, now it's [deprecated](http://www.swi-prolog.org/pldoc/doc/swi/library/arithmetic.pl) – CapelliC Apr 10 '14 at 10:06
  • @CapelliC Yes that is unfortunate. Also, they do not work with SWI's module system, so you cannot import/export them from within a module (which was [a recent question of mine](https://lists.iai.uni-bonn.de/pipermail/swi-prolog/2014/012873.html) on the SWI mailing list). – Wouter Beek Apr 10 '14 at 10:13
  • 2
    There's no need of explicit module qualification or to declare the predicate as a meta-predicate as `is/2` is a built-in predicate and built-in arithmetic functions also don't require module qualification. You also cannot define your own arithmetic functions in standard Prolog. – Paulo Moura Apr 10 '14 at 11:19
  • @PauloMoura I do define my own arithmetic functions [in SWI-Prolog](http://swi-prolog.org/pldoc/doc/swi/library/arithmetic.pl). This feature is probably not defined in ISO Prolog. (Assuming you mean "ISO Prolog" with "standard Prolog".) – Wouter Beek Apr 10 '14 at 11:41
  • 2
    As Carlo's pointed out, you're using a deprecated SWI-Prolog *only* feature. Better have an answer to the question that is universal. – Paulo Moura Apr 10 '14 at 13:36
  • I've now updated the answer based on feedback from Paulo and Carlo. Thanks a lot! PS: I want to convince Jan to keep the feature of user-defined arithmetic functions alive in SWI-Prolog, and otherwise make a package to this end myself. I think it is a very nice feature. – Wouter Beek Apr 10 '14 at 14:10
  • If, as you wrote in the SWI-Prolog mailing list, "every use of an arithmetic function outside the module that defines it in must be prefixed by that defining module's name", then why do you need the `meta_predicate/1` directive or the explicit module qualification in the head of the `operation/4` predicate? If the arithmetic function is called in its definition context, no explicit qualification is necessary. If the arithmetic function is called outside its definition context, then is already qualified so the extra qualification in your SWI-Prolog only solution should also not be necessary. – Paulo Moura Apr 10 '14 at 20:37
  • @PauloMoura My reasoning behind the explicit qualification in the head was that the module prefix should move from the operator (i.e. `Module:Operator`) to the assignment (i.e. `Module:(Result is Goal)`). My reasoning behind the use of `meta_predicate/1` was mainly for documentation purposes, it is correct that this is not needed for the predicate to work the way it does. – Wouter Beek Apr 10 '14 at 21:19
  • Did some experiments and indeed seems that moving the module qualifier from the operator to the `is/2` call (such that it takes place in the definition context of the arithmetic function) is **necessary** to avoid runtime errors as SWI-Prolog doesn't recognize Module:Operator as a valid function name. That's unfortunate but as this feature is deprecated no fix should be expected. – Paulo Moura Apr 10 '14 at 21:47
-3

You need to tell Prolog if Op is '+' then sum X and Y. You can do that as following

operation(X,Y, Op, Result) :- 
   Op = '+',  
   Result is X + Y
 ;
   Op = '-',  
   Result is X - Y.

You can increase number of operations.

false
  • 10,264
  • 13
  • 101
  • 209
gokhans
  • 210
  • 2
  • 12
  • Thanks mate! This definitely solves the problem. So in general there is no easier way to do this? – XerXes Apr 10 '14 at 08:04
  • As far as I know, there isn't better way. But it is just couple of lines for each operation :) – gokhans Apr 10 '14 at 08:12
  • 7
    Consider using `(=..)/2`. For example: `Expr =.. [Op,X,Y], Result is Expr`. – mat Apr 10 '14 at 08:49
  • 1
    @mat suggests an improvement. Another one, more basic: use `pattern matching`, i.e. `operation(X,Y, +, Result) :- Result is X + Y.` – CapelliC Apr 10 '14 at 09:37