Sunday, October 19, 2025

Arithmetic in CoPilot Prolog

I decided to stop work on implementing lists in my Prolog interpreter, and instead I concentrated on implementing simple arithmetic. Bratko had a query in the form ? - X is 1 + 2, but I don't like that syntax. I had seen in another Prolog book that addition was defined by means of a rule that was more to my liking:

add (Operand1, Operand2, Result):- Result is Operand1 + Operand2.

So one could issue the query ?- add (2, 3, X). and hopefully receive the astonishing answer that X = 5. As usual, this is deceptive simplicity. As opposed to other rules that might have chained goals (such as grandparent (X, Y):- parent(X, Z), parent(Z, Y).), the tail of this rule has to be parsed in a different manner. First of all, the goals within the tail aren't separated by a comma, but even worse, the body is a recursive expression where the operators are infix. In a similar manner to lists, this should result in the following PTerm: is (Result, + (Operand1, Operand2)). As I wrote previously, 'is' is the assignment operator.

Figuring out how to display the rule was exceedingly painful, but eventually I figured it out.

function PrintInfix (body: pterm): string; begin result:= removeprefix (body^.args[0]^.name) + ' ' + body^.name + ' '; if body^.args[1]^.kind = tkCompound then result:= result + PrintInfix (body^.args[1]) else result:= result + removeprefix (body^.args[1]^.name); end;

The problem here was figuring out where the operators are and where the operands are. Once this was out of the way, I could start work on actually solving the goals. The only thing that was clear to me initially was that I would have to add another case to the ResolveGoals procedure in order to handle the possibility that goals[0]^.name = 'is'. I saw that in similar cases (e.g. where the operator is '<') that first I resolved the values of two terms. The first term resolved to the variable in the rule, i.e. Result, but the second term resolved to something more complex. After examining the data in memory, I discovered where the operator was and where the operands were. These data were passed to a new Calculate function that simply returns the result of the calculation (e.g. '2', '3', '+' should return 5 as an integer); I included the code for subtraction, multiplication, integer division and modulo - each operator required only one line. At the beginning of this function, the first two operands are checked to see that they actually contain integers; if somehow a non-integral value were passed then the function would return 'false' (this would require something stupid like ?- add (noam, 3, X) which doesn't make any sense anyway).

Calculate did return the correct value, but then I was stuck: how did I return that value to the calling procedure for display? Here I invoked my co-author CoPilot again, and although he messed up a few times, I could see what the code needed to be.

So now I can run queries such as ?- divide (11, 2, X).   X = 5; and ?- modulo (11, 2, X). X = 1.

As it happens, I had no need whatsoever of the recursive infix expression parser as there weren't any expressions such as (2 + 3) * (4 + 5) to be evaluated. I can't really imagine a query that would require such an expression so I'm not going to waste any brain time on this.

Although ... yesterday1 I wrote about checking the time that one has to be at an airport prior to a flight and stated an expression  (T1 - Min) > 3. This does have need of an infix expression parser, although by the time the expression gets stored, it's not longer in infix form. It might be that because of the fact that the function Calculate should return an integer for T1 - Min, I could extend the 'greater than' operator to convert the second operand also to an integer in order to check their values. I'm not sure that '25' would be considered to be greater than '3', because '2' is generally less than '3'. 

I didn't mention yesterday that connections within an airport also take time that the naive database ignores. On the other hand, a database about trains could ignore those problems ... except for Milano Centrale, where the platform for the train to and from Malpensa is at one side, and of course the platform for Rapallo is at the other side of the station. It took at least 10 minutes (if not more) to get from one side of the station to the other, and when we returned from Rapallo, I also had to buy a ticket for Malpensa, causing even more of a delay. There would be no problems for Israeli trains, especially as I don't have to buy tickets anymore.

Internal links
[1] 2022



This day in blog history:

Blog #Date TitleTags
76619/10/2014Literature review: first draft completedDBA
126719/10/2019Juliet (naked) - the filmFilms, Nick Hornby
167919/10/2023Think again: the power of knowing what you don't knowHealth, Non-fiction books
184219/10/2024This year's headphonesHeadphones, Temu

No comments: