Sunday, January 18, 2026

Have I completed the Prolog interpreter? Take 2.

Just over a month ago, I wroteSo now I've exhausted all the goals that I originally wanted from a Prolog interpreter. It can solve the following queries (or parse the statements), each of which exercises a different part of the syntax as well as including goals that include multiple clauses. Of course, about a day later, I was adding more functionality to the interpreter.

Last week, I wrote2 that I was working with a Prolog program that plays the game 'tic-tac-toe'. The game at first was irrelevant; the program contained a few constructs that my interpreter could not handle. So once again CoPilot and I started adding what was missing: most of this was simple (for example, the i/o functions write/1, nl/0 and read/1) but one was extremely difficult.

It turns out that the simple comma has two different functions in Prolog. Until now, the comma separated arguments to a predicate; for example, the comma in the clause 'parent (noam, netta)' separates the two arguments to the 'parent' fact. But in another context, the comma behaves differently: this is when there is a list of goals to be solved. For example, the rule 'grandparent (X,Y):- parent (X, Z), parent (Z, Y)' contains four commas; three separate arguments, but the comma between the two parent clauses is actually an operator, in the same way that '=' is an operator. If this comma were not an infix operator, the body of the rule would be written as , (parent (X, Z), parent (Z, Y). It took a great deal of time to get the parser to parse an expression of this kind successfully. A casual user would be completely unaware of this problem.

Of course, after the parser succeeded in parsing the expression, the interpreter could no longer solve the rules; when solving, that compound statement has to be turned back into two separate clauses. This is similar to the manipulation performed on the goal list in order to implement the 'arg' predicate. This, at least, was relatively simple compared to the challenge of parsing the two conjoined clauses.

Now the tic-tac-toe program could load and I could get the computer to play itself. But when I added the necessary parts to the program to enable me to play as well, all manner of problems arose, most of which were due to the program itself and not my interpreter. In order to communicate the problem to CoPilot, I would take a screenshot of the offending part and upload it, but this seemed to me a poor way of communicating. Reading Clocksin and Mellish, they mentioned that the DEC-10 version of Prolog (probably long out of use) would provide a session log, consisting of all the statements entered and displayed.

I thought this a good idea, and during a dog walk, I considered ways of implementing this. When I put it to CoPilot, it had a better idea: create modified versions of write/writeln and readln, and use these instead of the normal procedures. My private procedures write both to the screen and to the session log; very simple to implement, and very useful. Now I could upload complete sessions to CoPilot for analysis.

Basically, we discovered that the Prolog program that I was using was missing some clauses, but not only that, it was quite stupid, in that it would choose the next available square in the grid: not a successful strategy. CP and I quickly implemented an evaluation function that assigns to each square a value which is the number of solutions in which that square can participate. On a 3x3 grid, each corner can participate in three solutions whereas each middle square can participate in only two; the middle square is the most valuable as it can participate in four solutions. I also wanted that the computer would announce its moves, instead of having to look at the grid and figure out what had changed.

Work finished on late Friday afternoon: it seemed that the program was tantalisingly close to completion but I had run out of time. On Saturday morning, I ran the program and saw that it was 99% complete, so with a few additions, we now had a Prolog version of tic-tac-toe that works quite well (the result is always a draw) as well as a much improved Prolog interpreter. Here's the session log for the final run:

> ?- consult (ttt). ttt consulted Yes. > ?- playo. You play X by entering integer positions followed by a period. [1, 2, 3] [4, 5, 6] [7, 8, 9] |: 1. [x, b, b] [b, b, b] [b, b, b] I play 2 [x, o, b] [b, b, b] [b, b, b] |: 5. [x, o, b] [b, x, b] [b, b, b] I play 9 [x, o, b] [b, x, b] [b, b, o] |: 3. [x, o, x] [b, x, b] [b, b, o] I play 7 [x, o, x] [b, x, b] [o, b, o] |: 8. [x, o, x] [b, x, b] [o, x, o] I play 4 [x, o, x] [o, x, b] [o, x, o] |: 6. [x, o, x] [o, x, x] [o, x, o] Cats game! Yes. > quit.

Incidentally, I didn't know what the expression "Cat's game" was supposed to mean. Could it be that someone whose nickname was Cat wrote the program and that this was her way of immortalising herself? No, says CoPilot. “Cat’s game” does mean something. It’s an old American expression for a tic‑tac‑toe draw. The idea is that when neither X nor O can win, the game is “for the cats” — meaning worthless, unwinnable, a stalemate. No cats were involved in the writing of your Prolog code.  I would have known this had I read the Wikipedia article more thoroughly.

I ran a new game: normally I would start with square 5 but just to keep things interesting, I decided to start with square 1 instead. The computer choosing square 2 was not clever; this probably means that the simple rule that chooses the next available square appears before the rule that used the evaluation function. As rules are evaluated in a top-down order, the position of a rule can be critical. After thinking about this, I rearranged the 'moves' rule so that they would appear according to their value; thus the first rule would cause the computer to choose square 5, then square 1, etc. When I ran the program with the rearranged rules, the computer played much better. Again, I chose to start with square 1 deliberately to see what the computer would do; it chose square 5. I then chose square 3, so the computer had to choose square 2, otherwise I would win with my next move. In doing so, the computer had set up the 2-5-8 combination, so of course I had to chose square 8. The computer then chose square 7 - this doesn't contribute anything, but it shows that the computer is playing according to the order of the moves: 5,1,3,7,9,2,4,6,8. As squares 5, 1 and 3 were occupied, the next unoccupied square according to the precedence was 7.

So once again I wonder, have I completed the Prolog interpreter?

Internal links
[1] 2047
[2] 2059



This day in blog history:

Blog #Date TitleTags
23018/01/2010Keith Tippett Group – "Dedicated to you, but you weren't listening"King Crimson
53818/01/2013Nic Potter RIPObituary, Van der Graaf Generator, RIP
91818/01/2016MP3 headphones (2) / The scientific methodProgramming, MP3
137018/01/2021Covid-19 vaccination (3)Covid-19

Saturday, January 17, 2026

Resurrecting films

Last week the family celebrated the second birthday of grand-daughter #3. Grand-daughter #1 had prepared a small quiz via the Internet site kahoot; somehow we had to display the questions on our tv. As this is a smart television, it is connected to our wifi network; like at work, I was able to connect to it with my work laptop ("connect to a wireless display"). For some reason, this doesn't work on my regular computer.

The next day I realised that I could show films to our grand-daughters by loading them onto the work computer then connecting to the tv. But first I had to get the films onto the computer. I have an extensive library of dvds containing films recorded from television, in days when I had a working dvd recorder. The work computer doesn't have a built-in dvd drive and the one on my regular computer doesn't work, so I dug out my portable dvd drive - one of my best purchases, from ten years ago.

This drive was able to read most of the dvds, so I would copy them onto the work computer then transfer them to my regular computer via vnc. This way, the file transfers are within my home network as opposed to transferring them via AnyDesk which would mean that the files would get transferred via the external internet, a slower process. Each dvd consists of eight to ten separate files; they have to be joined together to create a single mp4 file.

As it happens, I have a program suitable for doing this - Handbrake. I don't think that I've ever mentioned this program before, but it has proved useful when manipulating video files. All I need do is point Handbrake at a directory and start converting; the files are converted into a single mp4 file. Unfortunately this process is slow, taking somewhere between an hour and an hour and a half to convert.

Most of the discs that I fed the dvd drive were read without problems, but one stopped at the 90% mark and another would not be read at all. Today I remembered a trick that I used to use when vinyl records had a lot of surface noise: I would wash them lightly in water then dry them carefully. This worked very well with records, and to my surprise, it worked well with the dvds. The drive was able to read both of the problematic discs without bother.



This day in blog history:

Blog #Date TitleTags
32617/01/2011Copper socks 3Copper
53717/01/2013The birdsMIDI, Peter Hammill
119417/01/2019Under pressure but trying to escapeDBA, Holiday

Friday, January 16, 2026

A man and his dog


This somewhat surrealistic picture was taken yesterday afternoon when I was walking the dog. There's an area that is used as a parking lot for the high school and I often walk there before starting loops around the school's drop-off/pick-up area. The drainage is natural (in other words, there is none) so after heavy rain such as we had during the first half of the week, small lakes are formed that eventually evaporate and disappear.

From a distance, I could see someone crouching down by this lake and pointing their phone at the water, presumably taking pictures. I don't think that there would be any tadpoles so there didn't seem to be any attraction, but everyone is free to take whatever pictures they want. I did notice, however, that my shadow was cast on this person - the sun was behind me - so I tried to move in such a fashion as to move the shadow and so not effect the pictures.

About ten minutes later, when the dog and I had completed one circuit, a car hooted me: there was a woman inside with a young child. She told me that she had been taking pictures and one of them had accidentally framed me. I gave her my telephone number and she sent me the picture. It has exquisite composition, although I think that there's too much sky (maybe I'll crop this later).

I showed the picture to my wife when I came home. At first she refused to believe that it was me, but when I magnified it sufficiently to show my headphones and also see the dog better, she accepted my story and was so enthusiastic about it that she sent it to one of the kibbutz mailing lists and even uploaded it to her Facebook page. She received many "likes".

So thank you Meital Feine (that's how she spells her name) for that picture.



This day in blog history:

Blog #Date TitleTags
53616/01/2013Relaying the emailProgramming, Delphi, Email
91716/01/2016Vinyl log 26 - 16 JanuaryVinyl log, Randy Newman
100216/01/2017Priority trick for screen proceduresPriority tips
146316/01/2022Turning the DBA research around: the research journalDBA

Thursday, January 15, 2026

The Florentine entanglement

I have just had the pleasure of reading the newly published book "The Florentine Entanglement", written by Pamela Norsworthy. It's a book right up my street: a non-action Cold War spy novel with a certain amount set in Florence, Italy. To quote an AI, "The Florentine Entanglement" refers to a Cold War historical fiction novel, intertwining the 1960 U-2 spy plane incident with the complex marriage of a CIA officer involved, exploring how global events impact personal lives through the story of Eleanor and Talbot Bentley, set against a backdrop of espionage and wartime secrets from fascist Italy to the Cold War.

It is richly detailed and slow, so much so that at about halfway through the book, I started wondering where it was going to lead. I don't want to give away any of the plot, so I'll have to say that the first half gives the impression that the story is about a CIA officer who is involved with the U-2 programme, trying to understand how the Soviets could have shot down the plane, whereas the second half takes a dramatic turn elsewhere. Chapter 23 - the first chapter of part two of the book - suddenly takes the reader into an unsuspected world.

I enjoyed the book, despite its glacial pace. It is full of observation and details. It also lacks swearing which always makes a book easier to read for me. James Bond this is not, neither is it particularly George Smiley, although of course it does lean that way. Highly recommended.



This day in blog history:

Blog #Date TitleTags
44415/01/2012Tinker Tailor Soldier SpyFilms, John Le Carre
53515/01/2013Creating an email with a subject line in HebrewProgramming, Delphi, Email, Randy Newman
146215/01/2022Another Ninja experimentCooking, Ninja grill

Monday, January 12, 2026

The extra-tricky Prolog predicate 'arg'

Since my last blog1 on the topic, we (that's CoPilot) and I have implemented lazy solution generation (LSG) in my Prolog interpreter. We spent many hours on this, including several over the weekend, until this was working properly, to the best of our knowledge. This was hard work and at times quite frustrating as the solutions received for the same query (eg parent (noam, X)) varied from none to the same solution being displayed infinitely. Nevertheless, by Saturday afternoon, all the bugs seemed to have been ironed out and LSG worked properly. 

I thought that I had written about this, but apparently not. What happens now is that the interpreter provides one solution (assuming that there is one), then waits for the user to signal whether more solutions are required. If so, then the interpreter carries on from where it left off before in order to display a second solution. Previously all the solutions would be generated before showing them one by one to the user. The LSG is required for certain predicates to work properly.

After resting a little, I took the dog for a walk, and during this time considered that it would be good to implement a high-level tracing capability. Until now, there's been an internal interface for debugging but this is not suitable for 'the user'. So I put this to CoPilot and was told that now the LSG works properly, adding high-level tracing would be easy. Even so it took about an hour to get this working as I want it to be. Below is shown the trace of the above 'grandparent' query.

Call: grandparent (noam, X) Unify: grandparent (noam, X) Call: parent (noam, Z) Unify: parent (noam, netta) Call: parent (netta, X) Unify: parent (netta, shaked) Exit: parent (netta, shaked) Exit: parent (noam, netta) Exit: grandparent (noam, shaked) Redo: grandparent (noam, X) Redo: parent (noam, Z) Redo: parent (netta, X) Unify: parent (netta, lior) Exit: parent (netta, lior) Redo: grandparent (noam, X) Redo: parent (noam, Z) Redo: parent (netta, X) Fail: parent (netta, X) Unify: parent (noam, nir) Call: parent (nir, X) Unify: parent (nir, romi) Exit: parent (nir, romi) Exit: parent (noam, nir) Redo: grandparent (noam, X) Redo: parent (noam, Z) Redo: parent (nir, X) Unify: parent (nir, maor) Exit: parent (nir, maor) Redo: grandparent (noam, X) Redo: parent (noam, Z) Redo: parent (nir, X) Fail: parent (nir, X) Fail: parent (noam, Z) Fail: grandparent (noam, X)

Yesterday I spent some time reading what used to be (and maybe still is) the bible to Prolog: 'Programming in Prolog' by Clocksin and Mellish (aka C&M). I used to own a copy of this book but it disappeared in the great book purge from at least 10 years ago. Now I have it in PDF form which is not the most convenient format but I can live with it. I was looking for a 'real' program instead of the toy queries that I've been running and found a program for the Tic-Tac-Toe game. I looked through this program for built-in predicates that I have yet to implement. There were two: var, which is very simple, and arg which is far from simple. Like the tricky length predicate, arg comes in two flavours, as shown below.

Type A: ?- arg (2, parent (noam, netta), R). Type B: ?- arg (X, parent (noam, netta), R).

Seemingly, the only difference between these two types is that in type A, the first argument is a number whereas in type B, the first argument is an uninstantiated variable. In type A, the number 2 means 'return the second argument to the term parent (noam, netta) in the variable R'. In this case, R = netta. This was fairly simple to program, although it requires accuracy.

Type B however means something different and requires LSG to work properly. The number of answers depends upon the number of arguments in the given term; thus for parent (noam, netta), there should be two solutions, X = 1, R = noam, and X = 2, R = netta. This was extremely hard to program and was extra-tricky. There were two (actually three) difficult tasks to be achieved and I have to say that I had the insights to achieve them correctly.

CoPilot came up with the idea of entering temporary facts into the database then solving them with a temporay goal, but implementing this was far from simple. CP 'went down the rabbit hole' and made this much more complicated than it actually needed to be; I realised at one stage that all that was needed was two facts to be entered into the database - $arg_internal (1, noam) and $arg_internal (2, netta) - then a 'synthetic' goal needed to be inserted into the goal list, $arg_internal (X, Y), which would solve these facts for the required output. A user cannot enter this predicate because the functor name begins with the dollar sign which is illegal in the parser, but the interpreter can add these facts because it adds them directly without going through the parser. I verified that the goal could be solved with these facts.

The second task to be achieved was inserting the synthetic goal into the goal list.  This again went through several unsuccessful iterations until I realised that we were inserting this goal into a copy of the goal list and not into the real goal list that was being solved. As soon as this was understood, the task was easily achieved. Once the query is completed, the synthetic facts are removed from the knowledge base. I think that this removal can be optimised slightly but that will wait for another time.

This insertion of facts and goals is an intriguing technique, and now that it's been ironed out, it can be used again. I am not currently aware of any other predicate that does require this, but now the tool exists.

The third difficult task came about when I was trying to list the synthetic facts - although the clause itself existed, none of the terms within the clause were shown. As soon as I walked away from the computer for lunch, I realised why this is so. This actually is the same bug that happened a few weeks ago that occurs when the interpreter adds clauses to the interpreter when it is solving a query. This is connected to garbage collection; I won't go into details, but again this insight came from me and not from CP.

So CP may have the general idea of how to implement various predicates, but when things don't work, it doesn't have the insight to realise what is wrong. So CP and I make a good team.

Internal links:
[1] 2056



This day in blog history:

Blog #Date TitleTags
53212/01/2013Play it again, SamWoody Allen
79612/01/2015Removing the blinkers (Research questionnaire 6)DBA
79712/01/2015Reinventing the wheelDBA
91312/01/2016Sending complex emails via PriorityProgramming, Priority tips
188712/01/2025Recording "Life as a bell pepper"Home recording

Monday, January 05, 2026

Mud Dauber Nest

The other night, one of our neighbours knocked on the door, asking for some milk. "You've got a wasp nest outside your door", she remarked. I went outside, and above the lintel to our front door and to one side was a peculiar looking thing, attached to the ceiling.

Later on, I took some pictures and uploaded them to my friendly AI, CoPilot, for analysis. This is the response:

Thanks for sharing both images — they’re very clear and helpful. What you’ve got there is a mud dauber wasp nest.

🐝 What is a Mud Dauber Nest?

Mud daubers are solitary wasps that build nests out of mud, typically in sheltered spots like eaves, corners, or ceilings. The nest in your photos:

  • Is made of cylindrical mud tubes, each housing a single egg.
  • Has dark openings, which suggest that some adult wasps have already emerged.
  • Is not aggressive in appearance — mud daubers are generally non-aggressive and rarely sting unless provoked.

🧼 Should You Remove It?

If the nest is inactive (i.e., no wasps flying in or out), you can safely remove it:

  • Wear gloves and scrape it off with a flat tool.
  • Clean the area with soap and water to remove scent trails.
  • Consider sealing cracks or gaps nearby to prevent future nesting.

If it’s active, it’s best to wait until winter or contact a pest control professional.

Fortunately it's not active and I've never seen any wasps flying in or out. One can see in the right hand picture holes through which wasps have emerged in the past. A firm push by my wife with a broom handle dislodged the nest which I caught in a bucket. Indeed, it seems to be made of mud.




This day in blog history:

Blog #Date TitleTags
66605/01/2014End of year bluesERP
110405/01/2018Business rules in Priority which do not apply to specific users/2Priority tips
145505/01/2022Sandy Denny would have been 75 years old todaySandy Denny
157005/01/2023200 best singers of all timeSandy Denny
170305/01/2024End of an eraPersonal, Motorbikes
188505/01/2025Life as a bell pepper (song)Song writing, CoPilot

Sunday, January 04, 2026

Pedalboard/group practice at the beginning of 2026


I dug out the guitar pedalboard from where it's been hiding the past five months and dedicated some time to reattaching pedals that were hanging off the board. At the same time, I changed the position of some of the pedals as I removed the distortion pedal1 that I barely use and don't like. I added a pedal that arrived a few days after our last performance2: a noise gate pedal. Like the tuner pedal, it doesn't affect the sound; it simply 'turns the guitar off'. When the threshold is low enough, no signal will be passed through it. The idea is to prevent all the various clicks that the pedal board makes when pedals are turned on or off inbetween songs. I don't know whether I will keep this pedal as its utility is limited but obviously I identified a need. That's the white pedal on the far left - obviously this pedal should be at the end of the signal chain, which here runs from right to left.

Those who examine the picture very closely and compare it to an earlier picture (see the earlier referenced blog where I wrote about the distortion pedal) will notice that the power supply, the rectangular block on the top left of the board, has been rotated. I have noticed that the plug that charges the supply is always falling out and that it is hard to reconnect it as there is very little room between it and the left hand side of the power distributor. Turning this around should allow for better access to the required socket.

Last week we discussed which songs we would play in our first practice. 'Twist and shout' was the first number suggested, although I suspect that this won't be a keeper. I understood that the other song was called X by group Y, so yesterday afternoon I found this song on YouTube and notated the chords. I then printed the words to the song so that I could add the chord symbols, only to discover that the words that I had printed didn't match the words being sung. It turns out that this group has two songs whose names are almost identical. I had the chords to one song and the words to the other. Not only that, I was then informed that in fact we were going to play a completely different song by that group. I had to laugh when I heard this song as it rips off the chord sequence to the verse of Bob Dylan's "Lay lady lay". The song itself is quite interesting as the verse is in A whereas the chorus is in G. A modulation at the end brings the chorus up to A.

I was working with the Stagg guitar but it was very uncomfortable. I get the feeling that I swapped strings around - the B string is thinner than the E which is wrong. I'll restring the guitar yet again. My left hand had problems with the guitar: it felt like I was playing an egg slicer (like I used to do in my youth). I also had problems with the rounded neck, so I decided to take the Washburn guitar to the rehearsal. This was much easier on the left hand, but the guitar kept sliding from my lap as its body is much larger. No one commented that I was playing this guitar.

It seems that after my vocal debut last time, I will be adding the occasional backup vocal to songs. I have to admit that I found it difficult to continue playing whilst I was singing, although I'm sure that I'll get used to this.

Internal links
[1] 1798
[2] 1983



This day in blog history:

Blog #Date TitleTags
15504/01/2009Another day, another RDBMSProgramming, Delphi
79404/01/2015Less pleasant memories from early 1975Health, 1975
79504/01/2015Research questionnaire (5)DBA
110304/01/2018Business rules in Priority which do not apply to specific usersPriority tips
188404/01/2025David Lodge, RIPObituary, David Lodge

Friday, January 02, 2026

The tricky 'length' predicate

There hasn't been a Prolog blog for nearly two weeks, not because there was nothing to write about, but because I felt that there had been too many blogs about Prolog in a short span of time, and also I had hit a stumbling block that was causing many problems.

I wrote1 a few weeks ago length returns the length of a list, so a list like [a, b, c] has length 3 as does as a list like [a, [b, c], [d, e]]; this latter has only three elements, even though some of those elements are themselves lists. At the moment, I can't see what one would do with this predicate although presumably there is some reason. Later I was researching the farmer/wolf/goat/cabbage problem, and I saw in one of the solutions the statement length (List, 7). This didn't correspond to what I knew about this predicate. 

It turns out that there are two completely different usages for 'length': the first, which I had already encountered, returns the length of a list, whereas the second returns a list of n unbound variables, where n is the second argument to the function (i.e. length (List, 7) creates a list of seven unbound variables). It was clear that the code that I had for calculating the length of a list would not function at all for the second usage.

So CoPilot started working on what would provide this solution; during this period, we discovered here and there edge cases that had to be changed in order that length might create a list. At one time during this process, it seemed that the interpreter had entered an infinite loop. After analysing this, CoPilot informed me that the function (which was now implemented in Prolog and not built-in) was working correctly, but because of the design of my interpreter, it was creating an infinite number of solutions. After several more hours of struggle, the Prolog code managed to create a list as defined, but the original functionality had been lost. I decided to remove the 'length' code from the startup Prolog code and reconsider.

The infinite loop is due to the fact that the interpreter implements what might be termed eager solution generation, where all the solutions are generated before being displayed to the user, who can choose whether to display only one solution or more. This is fine when there is a finite number of solutions, as with the family database, but not in other cases, such as length (List, 7), despite the fact that one might think that it is not a problem to create a list of seven unbound variables.

CoPilot informs me: Lazy answer generation is about when the interpreter produces the next solution. For example:

?- parent(X, Y). X = john, Y = mary. More? y X = john, Y = david. More? y ...

This is the classic Prolog REPL behavior:
- Generate one solution.
- Pause.
- Only generate the next solution if the user asks.

This is incremental backtracking or solution streaming. Your current interpreter does not do this. It computes all solutions first, stores them, and then prints them one by one. That’s eager answer generation.

I decided that implementing 'length', as well as other tricky predicates such as '!', 'not', 'fail' and 'true' should wait until we implement lazy answer generation, probably later today.

In the mean time, I had been considering the 'length' predicate and its two capabilities. It occurred to me that I could implement it in Pascal without getting into all the problems that the Prolog code creates. If the first argument is a list, then the function should return the length of the list, and if the first argument is a variable then the function should return a populated list. My Pascal code as shown below derives a little from the original CoPilot implementation (the first two lines), but most of it is mine.

function ResolveLengthPredicate (goals: PTermArray; var env: TEnvironment): TArrEnvironment; var n: Integer; listTerm, lenTerm, head: PTerm; begin listTerm:= resolve (goals[0]^.args[0], env); lenTerm:= goals[0]^.args[1]; if listterm^.kind = tkCompound then begin // iterative length n:= 0; while (listterm^.kind = tkCompound) and (listterm^.name = '.') do begin inc (n); listterm:= listterm^.args[1]; end; if unify (lenTerm, number(n), env) then result:= ContinueSolving (goals, env) else setlength (result, 0) end else if listterm^.kind = tkVariable then begin // create list of variables according to lenterm head:= atom ('[]'); for n:= lenterm^.num downto 1 do head:= compound ('.', [variable ('_'), head]); if unify (listterm, head, env) then result:= ContinueSolving (goals, env) else setlength (result, 0) end else setlength (result, 0) end;

Once I had cleared the syntax problems, this code worked first time for both usages. I showed it to CoPilot who wrote:  I think you made a very smart engineering call, Noam — and honestly, it’s the same call that many Prolog implementers eventually make when they want length/2 to behave relationally without dragging in a full constraint solver. Let me break down why your Pascal implementation is not only reasonable, but actually elegant for the engine you’ve built. [I'm omitting the breakdown]. What CoPilot didn't mention what that 'his' original solution for calculating the length was recursive, in the same way that calculating the factorial2 is recursive. My implementation is iterative and much simpler as it simply steps through the array of terms.

During the night, it rained a lot; I awoke and listened to it for a while. My code for 'length' was running through my head, and I wondered whether there was a predicate for calculating the sum of the numbers in a list. For example, the sum of the list [1, 2, 3] would be 6. This should be quite easy to do using the paradigm of the 'length' code. When I got up, I googled 'prolog predicate calculate sum list', and discovered that in Prolog, the standard built-in predicate for calculating the sum of a list is called sum_list/2. If  one's version of Prolog doesn't have this predicate, it can be implemented as follows, normally using an accumulator for efficiency.

% Main predicate: initializes the accumulator to 0 and calls the helper. sum_list_acc(List, Sum) :- sum_list_helper(List, 0, Sum). % Helper predicate base case: when the list is empty, the final Sum is the Accumulator. sum_list_helper([], Acc, Acc). % Helper predicate recursive case: % Calculate NewAcc, then recurse with the Tail and NewAcc. sum_list_helper([Head|Tail], Acc, Sum) :- NewAcc is Acc + Head, % Calculate the new accumulator value sum_list_helper(Tail, NewAcc, Sum). % Recurse with the new accumulator

But I'm doing this in Pascal and in doing so, I am avoiding any problems that the Prolog implementation might create. There are another two very similar predicates, each of which I implemented in about a minute: 'min_list' and 'max_list'. To be honest, these predicates are 'nice to have', but I can't imagine where they would be used. I also feel that this kind of predicate is leading me towards programming in a procedural style and not in a logical style, which is the whole point of Prolog. So I think that the next steps are to implemented easy solution generation and then handle those tricky predicates that I mentioned earlier.

Part of the discussion with CoPilot that seems to have disappeared is about existential variables. To quote the AI that Google displays: In Prolog, "existential variable" is not a standard built-in concept for general programming, as all variables in a standard clause are considered universally quantified by default. The term is specifically used in the context of certain built-in predicates like bagof/3 and setof/3 to perform existential quantification over a goal. In standard first-order logic, an existential quantifier means "there exists at least one value" for which a statement is true. I don't claim to completely understand that but it does bring memories back of French O-level studies in 1971.

The O-level exam was split into two parts (held on different days): the first part was written, a normal exam paper, but the second part was oral, where one of our teachers would interview us whilst recording the conversation so that an external examiner could evaluate our ability to converse in French. We had a little freedom in terms of the topic to be discussed: there would be four options available, but only two would be chosen, from which we had to chose one topic on which to talk. This meant that in practice we only had to prepare three topics.

The well-named Jonathan Blaise Duke-Evans (aka "The Duke") [years ago I discovered that he was a senior official in the Home Office] sat in front of me in class and was always the source of interesting topics for discussion. He suggested apropos of nothing that I prepare a topic about Jean-Paul Sartre; this seemed off-the-wall enough that I took him up on this and learnt all about JPS, Simone de Beauvoir and existentialism. I don't remember now what my second topic was; the third was food but again I don't remember what the fourth topic was, for which I didn't prepare anything. In the exam, I had to chose between food and the non-existent fourth topic, so of course I talked about food. The only thing that I remember about this was that I couldn't remember what potatoes are called in French (pomme de terre); the teacher tried to help me by suggesting alternatives but I stuck to condemning myself. I still passed the exam. Anyway, whenever I hear about existential qualifiers, I always think of Jean-Paul Sartre.

Internal links
[1] 2050
[2] 2033



This day in blog history:

Blog #Date TitleTags
22602/01/2010Neat tricks in the 'management' program/2 - MDIProgramming, Delphi, MDI
22702/01/2010Does Satie have friends in Hollywood?Satie
32202/01/2011Suite: Judy Blue EyesCSN
44002/01/2012The difficult negotiatorNegotiation
79202/01/2015Painting with numbersStatistics
170102/01/2024Back to squabblingIsrael