Friday, December 05, 2025

Hazy concert memories

For the past few weeks, on and off, part of a lyric to a song by James Taylor has been running through my mind: And I feel fine all the time when she is with me [these aren't the actual lyrics but very similar]. I didn't know which song this came from, so eventually I asked CoPilot, who immediately answered with "Something in the way she moves". I listened to the song but it didn't sound familiar; I wonder how those words entered my memory. The fact that I "remembered" wrong lyrics means that I didn't listen to the song closely.

I then asked CoPilot about the semi-mythical concert that I attended in March 1970, with a triple bill of Fotheringay, John and Beverly Martin, and Nick Drake. Although I remember nothing about the concert itself, I know that Fotheringay appeared, as did J&BM1. I have always wondered whether Nick Drake appeared, as he was shying away from public performances. CoPilot had the following to say (edited)....

Yes — Nick Drake did perform at Colston Hall, Bristol on 22 March 1970, supporting Fotheringay. As for John & Beverly Martyn, they were billed as part of the same concert, but evidence about whether they played with a backing band is scarce; most accounts suggest they appeared as a duo, though John Martyn was occasionally accompanied by Danny Thompson (double bass) around this period. [This doesn't match with the JM interview that I quoted in the earlier blog, "We only had four days to rehearse with the band,  although the other gig at Bristol was strangely successful."]

🎸 Nick Drake’s Performance

  • Confirmed appearance: Setlist.fm and concert archives list Nick Drake as performing at Colston Hall on that date.
  • Historical note: Drake was famously reluctant to play live; this tour was the only structured set of concerts he ever undertook, totaling just five shows.
  • Reception: Accounts suggest his sets were short and subdued, often with little audience response, which may explain why memories of his performance are hazy.

📜 Why Documentation Is Sparse

  • Nick Drake’s live appearances were rarely reviewed in detail, and John & Beverly Martyn were less documented than John’s later solo career.
  • Surviving material includes a rare Fotheringay tour program and scattered fan recollections, but specifics like backing musicians are often missing.

Nick Drake really did play that night in Bristol in March 1970. It’s one of those rare, almost mythical live appearances, since he only ever did a handful of concerts in his career. That Colston Hall show with Fotheringay and John & Beverly Martyn is part of the very short tour he undertook, and it’s now remembered as one of the few times audiences got to see him on stage.

It’s fascinating that you were there — even if the memory feels hazy, you were present at one of the most elusive chapters in British folk history. Many fans today only know Nick Drake through his records, so being able to say you saw him live is quite special.

I also attended a few other mythical concerts: one was Pink Floyd in late 1970, when they toured "Atom Heart Mother" along with an orchestra and choir, and another was the 50-piece Centipede in March/April 1971. I have a very special memory of that concert: if I was going to a concert on a Sunday evening, I had begun going down to the Colston Hall in the early afternoon to watch the bands arrive. That day, I saw a coach arrive and several musicians disembark, amongst them Robert Fripp, who was somewhat legendary in my circles at the time. I was too shy to approach them, though. Later in the evening, I was behind the band speaking to someone who had a seat at the rear (not a good place to watch a concert IMHO), when RF turned around, saw me and waved. I think that I've written this before somewhere. I also attended the July 1971 concert with James Taylor and Carole King. 

Regarding that concert with Fotheringay: I have conflicting memories about this. Sometimes I think that the Humblebums (Gerry Rafferty and Billy Connolly) appeared and not J&BM. The confusion stems from the facts that the Humblebums did support Fairport when I saw them a month or so earlier, and their name appears in that rare concert program, not J&BM.

As I wrote to CoPilot, if memories were antiques then I would have a rare collection. Unfortunately, memories fade and become hazy, whereas antiques last.

Internal links
[1] 1417



This day in blog history:

Blog #Date TitleTags
11205/12/2007Sleep, part twoApnea
52405/12/2012Post mortem on the Research Proposal examDBA
65605/12/2013Post mortem on IBR3 examDBA, Statistics
90505/12/2015Tuning the pianoMusical instruments
144505/12/2021The week of the virusHealth, CPAP, Non-fiction books

Sunday, November 30, 2025

Return of the infix operators (Prolog 8)

Yesterday I worked for a few hours on the Prolog interpreter, primarily restoring the infix operators that had temporarily been removed. As opposed to last time1, there were no nasty problems that these operators caused. That said, several interesting problems occurred. Compilation with Delphi 10 did not cause any problems except with logging; this routine had to be replaced. But the resulting file is about 10 times larger with D10.

The first problem that occurred was with the token 03:30; the tokeniser was turning this into three different tokens that of course wasn't what I wanted. Another problem that was easily fixed was with the rule divide (Operator1, Operator2, Result):- Result is Operator1 div Operator2. The parser crashed on the first token 'divide' as I had added 'div' as a special token. Even the factorial function worked perfectly.

After the factorial finished, I naively asked whether the token 24 (which is factorial 4, or 4!) needed to be added to the symbol table. CoPilot ran with this and suggested that we define a new kind of term, a numeric literal. This caused many problems as several functions had to be updated to take this new kind into account. It also solved a problem that had yet to arise, in two comparison functions, 'greater than' and 'less than'. These function were comparing the alphanumerical value of terms, eg '11' and '2'; when these values are compared, '11' is less than '2', which of course is not what is wanted. So comparing the numbers gave the correct result. Until now, the 'greater than' function had never seen a number greater than 9, so there was no problem comparing 9 to 0. 

Once everything was working correctly, I thought that I would try the next type of problem that the interpreter has to solve - lists. First I entered a (meaningless) fact, p([1,2,3]). Then I queried ?- p ([Head|Tail]). To my surprise, the solution Head = 1, Tail = [2,3] was presented. Correct! I then defined the 'member' rules

member(X, [X|_]).
member(X,[_|Y]):- member(X,Y).

Then I ran queries like ?- member(d,[a,b,c,d,e,f,g). (yes) and ?- member(2,[3,a,4,f). (no). I was quite surprised that these queries worked without having to add any special code. But the 'append' rules didn't work, so obviously there is work to be done regarding resolving lists. This will wait until next week.

Internal links
[1] 2037



This day in blog history:

Blog #Date TitleTags
14830/11/2008Criminal JusticeTV series
65330/11/2013Arik EinsteinIsrael, Obituary
98830/11/2016Backing up dataComputers
155730/11/2022Mid-night revelationsDBA

Saturday, November 29, 2025

Restringing the 12 string guitar

It seems to me that I spend more time tuning the guitar than I do playing it. A few weeks ago, I was tuning it once again when a string broke - this was probably the high G string, the highest of all the strings and the one under most pressure. I wasn't overly surprised; in the eight years1 that I've owned this guitar, I've never changed a string. So I ordered a set of strings for a 12 string, and they arrived a few days ago.

Before putting the new strings on, of course I had to remove the old ones. This was quite easily done; first I reduced the tension on every string and then I cut it. One half of the string had to be unwound (as on an electric), but the other half had to have the pin that holds it removed first. This isn't something that is very easy to do; the pins have to be firm in order to anchor the strings. I used a pair of pliers to extract the pins and the second half of the string.

I then cleaned the guitar thoroughly, getting rid of all the accumulated dust and grime, eventually oiling the fretboard. I left the guitar unstrung for about half an hour before I started putting the new strings on. I think that for the octave strings, the high string is supposed to be below the low string - if one strikes from top to bottom, then first the high E string will be struck (the 12th string) and then the low E string. I decided to change this order and have the low E string first. I suspect that the bridge and nut weren't cut for this possibility.

As I was restringing, I remembered that one of my first orders from Temu was a string winder. I've never used it (it was still in its wrapping) and at first I wasn't sure how to use it. The gap on the head fits onto the machine head, then one turns the handle in a rotary fashion, like a rattle. This makes turning the head much easier, and I found that I had to turn the heads a great deal. The winder also is supposed to have the capability of removing the bridge pins, but as I had already taken these out, I couldn't test this.

I tightened the strings to a moderate level at first; then a few hours later I came back and tuned the strings more accurately. At the moment, all the strings (bar that pesky high G) are near enough in tune - at least, they're at the approximately correct tension but not necessarily exactly in tune. That high G string keeps on slipping but hopefully it will soon settle down. I think that I'll order another set of string shortly, not because I think that a string might break but because I may restring the guitar again in a few months, this time with the order of strings (high vs low) reversed.

Internal links
[1] 1051



This day in blog history:

Blog #Date TitleTags
52229/11/2012Uncle once more!Blood pressure, Uncle, Acupuncture
98729/11/2016End of November/TV seriesTV series, Cold feet
127729/11/2019A 'new' DVD recorderDVD
136029/11/2020New favourite drinkPersonal
186729/11/2024Goodbye F25Mobile phone

Friday, November 28, 2025

A bad week for computers

On Sunday, I accompanied my wife to Tel Aviv so that we could sort out some of the inheritance problems arising from my brother in law's will. We had to go to an address in Yaffo (southern Tel Aviv), so unthinkingly we took the train to Tel Aviv, got off at the first station and took a taxi to our destination. It was a long ride. When we finished our business, I saw that the Tel Aviv metro ran very close to where we were, so I checked the route and saw that it would take us to the central train station. When the train came, we alighted and looked for a device where we could clock our travel cards, but we couldn't find one. As both of us can travel on public transport for free, we weren't too bothered. After a stop or two, the train went underground and after a few more stops we arrived at our destination: just like being abroad! There were automatic ticket machines there, but they wouldn't let us out. We explained to the guard who came to check what was happening; she explained that at overground stops, the machines are in the 'bus' shelters; we simply hadn't looked there (this would be like in Italy). As we don't have to pay for the trip, there was no penalty, but someone before us who also didn't 'stamp his card' was charged with a fine.

Reading text messages on our phones, we discovered that there was a major electicity blackout in our area after a pole had caught fire. There was an urgent problem with Priority that had to be fixed so I had no choice but to call the network manager and talk him through the correction that needed to be made. Later I discovered that instead of writing an equals sign (=) in a condition, I had written an unequals sign (<>)! A small difference with a major effect! 

The power came back on at around 16:30, which is when I discovered that my XP computer refused to boot. I assumed that the power supply had gone. The next evening, I took that computer along with my mobile to our local repairman: the mobile worked fine, but its fan would frequently get stuck, thus causing the computer to overheat. There was also a problem with the lid. In the mean time, I used the computer that I have from work for my daily activities: this is fine for working, but I don't have that many books or much music on it. 

After a few broken communications, the repairman told me that the XP computer was completely dead; apart from the power supply, there were several capacitors that had blown. "It's OK", I told him, "I have a reserve computer here for exactly this contingency. Can you move the disks from the old computer to this reserve one?" "Send me a photo of the computer's board", he replied. I did so, only to be told that several of the capacitors on this computer also were dubious. This morning I connected the computer to the peripherals, but it too would not boot. It's a real problem to find a board that is old enough to support XP these days. If the repairman doesn't find one, maybe I'll use a more modern computer and try to build a virtual machine that at least will support Delphi 7. I don't know what I'll do about music.

I picked up the mobile an hour or so ago. It has a completely new case which on one hand is good, but on the other hand, I've lost a USB port. There are now only two which are on the left hand side, making life problematic for the mouse. I'm using an extension cable at the moment.

The work computer could not access emails sent to my home computer, whereas my mobile computer can. There were a few blog entries than needed to be uploaded into the blog manager, but the HTML code that the modern version of Outlook creates is not good for the blog manager. After a bit of experiment, I discovered that I can input the HTML code: almost nothing gets entered into the blog manager, but then I can copy the HTML code directly from blogger and everything is fine.

I discovered during the week that I could not connect via Anydesk to the OP's server; problematic as I had been sent an email about a problem whose source was oblique. Eventually I swapped messages with someone there who has a certain amount of technical nous: I saw that the clock on the server was showing some date in 2040. After he corrected the date, I was able to connect to that computer. There were several entries in one table that had completely wrong dates - this was the source of the problem. I hand corrected those entries and now everything more or less seems to be ok there. 

I don't think that there'll be a Prolog blog this week. I'll try and bring the code up in Delphi 10 Seattle that I have on this mobile; it should be fine, but there may be a required change in syntax here or there. Programs created by this version are about 10 times larger than under Delphi 7 and I find the programming environment to be very cluttered.

This day in blog history:

Blog #Date TitleTags
31328/11/2010Sumptuous Sunday 2Cooking
109428/11/2017Research proposal accepted!DBA
118828/11/2018Executing a program and waiting for it to finishProgramming, Delphi

Sunday, November 23, 2025

Prolog vs SQL

Whilst writing the final sentences of my previous blog about how something would be easier to do in SQL, I started thinking about the representation of data in Prolog. It started by my adding a fact, cd ('Liege and Lief', 'Fairport Convention'). If I queried which Fairport cds were in the database, then only one cd (Liege and Lief) would be found, and if I queried which Fairport cds are owned by somebody in the database, then again only one cd (Unhalfbricking) would be found. As CoPilot puts it, Right now, your database only knows about owns/2 facts, and the embedded terms like cd(Title,Artist) or book(Title,Author) are just data structures inside those facts. If you want to query them directly, you need a mechanism that “lifts” those embedded terms into their own predicate space.

In SQL, there would be a table of cds and a separate table of ownership whose primary key would be a composite of a person and a cd. Other data such as date of purchase can be added. Relational databases have the key property that every datum is stored only once, and if that datum is needed elsewhere, then it is referred to (that's the 'relational' part).

But Prolog isn't like that and I have to remember that it's not a relational database. If I own a copy of 'Unhalfbricking' and Yossi does as well, then the two clauses about the cd are not the same, although they should unify. I wonder whether it would be possible to have facts like cd (Key, Title, Artist), so that 'Unhalfbricking' could be stored as cd (1, 'Unhalfbricking', 'Fairport Convention). Then there could be  clauses such as owns (noam, 1) and owns (yossi, 1). But I suspect that the concept of an SQL join does not exist in Prolog.

So what can I do to query both a list of cds and to show who owns what? There are various solutions to this, either by writing a rule in Prolog that stores embedded clauses as facts in their own right, or even adding the necessary code to the parser. This is a bit irregular but possible. As they say, it'll be a righteous hack. I did add the necessary code but added a global flag to prevent this, should the user so desire. When adding such a fact to the database, it should be checked that it doesn't already exist; Prolog has no problem with duplicate facts but it tends to ruin solutions.



This day in blog history:

Blog #Date TitleTags
30923/11/2010The gravy boatCooking
31023/11/2010Copper socks 2Health, Copper
109323/11/2017Two more people have passed awayObituary
186423/11/2024Management email problemsProgramming, Delphi, Email, Threads

Saturday, November 22, 2025

Another weekend goes by in the life of my Prolog interpreter

I don't want to give the impression that I do nothing else but work on this Prolog interpreter. I am still employed full-time and have been working on many things (some of which found their way to 'the other blog') but there's been nothing to write about here.

After the problems of last week, I decided it would be better to take a step back in order to take a step forward. So all the code that is connected to infix operators (basically the arithmetic operators) was removed, and we (CoPilot and I) wrote a new and cleaner parser that works on prefix operators. Both the parsing and the displaying are now clear, clean, elegant and recursive. No changes were required in the goal solver.

Previously I showed a database that earlier versions were not capable of parsing.
owns (noam, cd (unhalfbricking, 'Fairport Convention')). owns (sarit, cd ('Best of', 'Dire straits')). owns (noam, book ('Programming in Prolog', 'Clocksin and Mellish')). owns (noam, book ('The double helix', 'James D. Watson')).

This week's goals were to handle quoted names and to handle nested clauses, as shown in the above database. This wasn't particularly difficult, requiring only a few additions in the parser. But once this was done, I started thinking about how these facts could be queried. For example, I can see which items I own by means of the simple query, ?- owns (noam, X). But a more interesting query would be to list all the people who own a book. A naive query would be ?- owns (noam, book (_,_)). The underscore character, _, means that it doesn't matter what the title of the book or its author are; this is called the anonymous variable. Unfortunately when I issue this query, I get two results, both of which are noam.

In order to get only one solution, I could use the cut (!) operator, with which I never really got to grips with when I was learning Prolog. It's true that appending the cut to the book query gives me only one result, but when running the equivalent query with cd, only one result is returned, because of the cut.

The sophisticated way of doing this is via the intrinsic setof operator that takes three arguments. The query to show who owns a cd would be ?- setof (P, owns (P, cd (_, _)), People). The result of this is People = [noam, sarit]. Implementing this operator required a new unit as well as making a change in the code that decides to show the values of which variables after solutions are found. 

Once this was under my belt, it occurred to me that it would be good if there were a rule such as who_owns (What, X):- setof (P, owns (P, What (_,_)), People). In other words, to parametrise the internal functor. This is possible but requires a whole load of fiddly code that I am not interested in, to be honest. So that is tabled for a later date. The solution also requires handling an infix operator, and at the moment, infix operators are out of scope. 

Previously I might have compared Prolog with SQL; apart from anything else, both are declarative languages. SQL would have no problem in showing who owns what, primarily by means of the 'group by' directive, but this is much harder to do in Prolog.

The next stage obviously will be restoring the infix operators with all their attendant problems.



This day in blog history:

Blog #Date TitleTags
14622/11/2008Surreal momentKing Crimson
30822/11/2010The road to AntibesMIDI, Song writing, Soundclick, Antibes
65222/11/2013Nostalgia (song festival)MIDI, Kibbutz, Song writing
109222/11/2017One more instrument in the developer's toolkitPriority tips

Tuesday, November 18, 2025

Implementing CP Prolog - 8, an epiphany

 On Sunday, I wrote: During the week, I had been thinking about how I had become addicted to programming this interpreter; yesterday was certainly addiction, but it wasn't much fun. At the moment we're at the stage of clutching at straws with no fresh ideas of how to solve this problem that is down to memory management, something that Delphi - and expecially the very stable Delphi 7 version - is supposed to handle for the user.

Last night when I got into bed, I started thinking about the parser - not a recommended way of falling asleep, I note - when I had an epiphany. At some stage on Saturday, CoPilot had suggested saving the value that the parser was returning in a global variable or similar. This isn't a very good idea in terms of computer science and we ignored it. But last night, I realised that pointers to all the terms were being saved in the program's heap so that they could be disposed of during garbage collection. All I needed to do after parsing a clause in the rule's body was to walk down this heap, looking for the first (actually, the last) term of a specific type - this is the value that should be saved. CoPilot would never have 'thought' of this.

What does this mean in simple terms (pun not intended)? Let's say that there is the clause female (X). The parser will read the token female, and because of the bracket, it will know that this token is the functor of the clause. The token X will be then be read and a new term allocated (and stored on the heap) for this variable. Then a term will be allocated (and again, stored on the heap) for a compound item, consisting of the functor (female) and its arguments (X). So if I want to get the value of this term later on, it will found at the top (or close to the top) of the heap.

This solution, whilst quite brilliant, is sidestepping the actual problem that is preventing the proper execution of the code - but I'm prepared to accept the pragmatic solution if it keeps me moving forward. Incidentally, after using the value provided by walking the heap, the entire body was displayed correctly, which is a big step forward.

Next is checking that this rule can be resolved, just to ensure that everything is as it should be. Then will come the task of parsing a fact that has nested clauses such as owns (noam, cd (unhalfbricking, 'fairport convention')). I'm fairly sure that all I will have to do is change one statement in order to achieve this. I wonder whether the result of parsing these nested clauses will also display the same bug that I've just fixed (or at least, worked around). 

But I'm going to beat my addiction and not touch this material for a few days.

Internal links
[1] 2037



This day in blog history:

Blog #Date TitleTags
14518/11/2008Lonely at the topMIDI, Randy Newman
30518/11/2010How to save money when ordering books from abroadBook depository
30618/11/2010Alesis Q49MIDI, King Crimson, Antibes
52118/11/2012Warming up for DBA examDBA
77718/11/2014Enron's spreadsheetsDBA, Excel
155218/11/2022Fairport newsFairport Convention
168718/11/2023The musical group reconvenesMusical group
186218/11/2024Shel Talmy, Peter Sinfield RIPObituary, King Crimson, Van der Graaf Generator

Sunday, November 16, 2025

Implementing CP Prolog - 8, a problematic start

What, I hear you say, a weekend without an update on the status of CP Prolog? Well, yes, that's because I spent many frustrating hours over the weekend trying to finish the first part of a new version - and not succeeding.

The impetus for the new version was a set of clauses that I saw in a book, along these lines 

owns (noam, cd (unhalfbricking, 'Fairport Convention')). owns (sarit, cd ('best of', 'Dire Straits')); owns (noam, book ('nightwings', 'Robert Silverberg')).

In other words, I want to add the possibility of nested clauses, and also have quoted names. This will enable me to get a list of all the items that I own, or a list of cds, or even a list of cds by Fairport Convention.

To this end, CoPilot built a recursive descent parser along with a tokeniser that worked perfectly with prefix operators. I was initially pleased as this parser would replace the rather ad hoc parsing that existed so far. Unfortunately the pure model of prefix Prolog is ruined by those pesky infix mathematical operators, so first I had to add some code to the tokeniser to recognise these, and then some code to the parser. This last part was quite difficult; there was something not quite right with one of the functions which of course was the one that dealt with the infix operators. I added a patch which seemed to fix the parsing of some expressions but not all. 

I ran out of time on Friday afternoon so I had to continue on Saturday morning with a fresh brain. I soon found the correct implementation of the fix (if you really want to know, the parser was confusing the comma between clauses with the comma between operands), and later found that this code caused problems right at the very end of the list of clauses so another condition had to be added. This was my work, not CP's; I had to 'argue' with it until it conceeded that I was correct.

The parser now reads a list of clauses well, although we haven't trained it on those nested clauses at the beginning of the blog - these are on the 'head' side of a rule and not the 'body' side. I am hoping that one simple change will allow the program to read those nested head clauses.

After I finished with the parser, I idly thought to display the clauses that had been parsed, just to check that everything was correct. And not only was the output incorrect, it had somehow missed the first two clauses out of five, the third was repeated as the fourth and only the fifth was correct! So CoPilot and I started a debugging marathon of several hours that has yet to be completed. It's completely weird that for every clause except for the final clause in a list, one function can be seen to return a certain value, but that value gets mangled by the time that it arrives at its destination. Very strange. CoPilot was convinced for some time that the problem was caused by something overwriting something else, but I told CP that it was wrong, for if that were the case, then all the output would be identical. Again, it eventually conceeded that my explanation was correct

There wasn't much else to do yesterday as it was pouring with rain (30.2 mm) so we continued and continued. During the week, I had been thinking about how I had become addicted to programming this interpreter; yesterday was certainly addiction, but it wasn't much fun. At the moment we're at the stage of clutching at straws with no fresh ideas of how to solve this problem that is down to memory management, something that Delphi - and expecially the very stable Delphi 7 version - is supposed to handle for the user.

Once the parsing and memory management is fixed, then I will check whether displaying the knowledge base needs improving. And only then will it be possible to start on improving the goal solver.



This day in blog history:

Blog #Date TitleTags
64716/11/2013Song festivalMIDI, Kibbutz, Song writing
135816/11/2020Vinyl log #32Vinyl log, Heron
168616/11/2023Counting beats with Van der Graaf Generator (4) - Whatever would Robert have said?Van der Graaf Generator
186016/11/2024New CPAP mask (2)CPAP

Saturday, November 15, 2025

New CPAP mask

I received my new Joyce Cara CPAP mask on Wednesday and immediately its effects were felt, reducing the number of apneic events from nearly 20 per hour to only 3 (and there's room for improvement). My original hypothesis - that I discarded - was that the mask needed to be replaced; this is the real reason for the increase in apneic events. The mask came with the straps already attached; all I had to do was tighten them. I think that in previous years I have had to attach the straps myself, and my three left hands mad this process difficult. 

Last year, I wrote that the company was offering the mask for 799 NIS on its website; the price is now 749 NIS. I spoke to a sales person about this and discovered that this was the price of the mask itself, without the flexible pipe that connects to the mask and brings the air from the machine, and without the filters for the machine. I didn't use to change the filters much in the past (although I do now) and I don't know how much the pipe degrades over a year - if at all - but as I'm not paying anyway, it didn't make much sense to try and save. As my wife said when we were talking about some other health issue, don't scrimp on health.

My weight has also reduced by 300 g this week - not a huge amount, but still considerable. The family came over for dinner last night and we talked a little about the diet. I said that I wasn't used to eating at 7 pm and indeed I had stomach ache after dinner. They asked whether I was hungry in the evenings - I said that I frequently feel an urge at tea time for a little something but I can successfully resist this. I don't know what I'm going to do the next time we have a Friday night dinner: either eat much less or else cook a portion of fish and eat that. Friday night communal dinner is one of the great Jewish traditions.

The first rain fell yesterday and woke me up at 4:45 am. Shortly after I got up and took the dog for her morning walk. It started raining again at about 11 am and didn't stop until the evening, so fitting in an afternoon or evening walk was problematic. Before 8 am yesterday, 0.4 mm fell, and until 8 am this morning another 4.9 mm. Not huge figures but it's a start. The rain is supposed to finish by tonight. 

Internal links
[1] 2024
[2] 1853



This day in blog history:

Blog #Date TitleTags
14415/11/2008Adele
30415/11/2010Project Management lecturesMBA, Project management
51915/11/2012Inside the DOCU program (2) - saving and loading rich textProgramming, Delphi
52015/11/2012Inside the DOCU program (3) - Saving screenshotsProgramming, Delphi
90315/11/2015Going to see a man about a dogDog

Monday, November 10, 2025

Implementing CP Prolog - 7A, supplemental

I wrote on Saturday1: I had to take out several of the instrinsic functions that were added last week, so now these have to be restored, and there's no guarantee that the code that worked a week ago will work now because several aspects have been changed and tightened up.

I restored the intrinsic functions necessary for the 'time_in_minutes' query2 on Saturday evening. No syntax errors occurred when I did this, but running a query like ?- time_in_minutes (03:30, X). should give the result 210. Unfortunately the result was 270. Looking at the trace of the query, it was clear what happened. The final goal of the rule is X is H * 60 + M, where X is the result, H the number of hours and M the number of minutes. Instead of calculating (H * 60) + M (i.e. (3*60) + 30), the interpreter was calculating H * (60 + M), or 3 * 90. 

So we had to reimplement the parsing of a line that could have any number of infix operators. CoPilot went through about four different implementations until we finally got something that would work correctly. On and off through the evening, I considered the final implementation and was sure that it was more complex than necessary. There was also a bubble sort at the beginning of the function that could be avoided with a little preplanning (and it's just occurred to me that the bubble sort would run each time the parsing routine was called which is several times: the sort should have been moved out of the function). 

I gave my conclusions to CoPilot and they were sort-of incorporated. At least another two versions were required before the statement was parsed correctly. It's all due to operator precedences  - remember from junior school: multiplication and division before addition and subtraction. Just to make things more complicated, (I quote) In Prolog, operator precedence is a numerical value (typically between 1 and 1200) that determines the order in which operators are evaluated within a term. Higher precedence values indicate weaker binding, meaning operators with higher precedence are considered "further out" in the structure of the term, while lower precedence values indicate stronger binding. For a language of logic, it seems illogical that a higher number has what one would normally call lower precedence. This required several loops to run backwards, although it occurred to me afterwards that it would be more sensible to change the operators table once again so that the loops could run normally. Ultimately it doesn't make any difference.

Once again, I have to say that for all the sophistication of CoPilot, sometimes it makes silly mistakes and sometimes the results aren't what were required. One "endearing" feature that I have noticed is remarks like "there's a problem with your code at such-and-such a place". Sometimes it's typing errors, but more often those are mistakes that CoPilot made in early iterations.

Internal links
[1] 2033
[2] 2029



This day in blog history:

Blog #Date TitleTags
77310/11/2014Literature review: second draft completedDBA
98510/11/2016Data collectionDBA
109010/11/2017INTQUANT and REALQUANTPriority tips
185510/11/2024F25 Cell phone stand with wireless speakerMobile phone

Sunday, November 09, 2025

James D. Watson RIP

James Watson was one of the pair 'Crick and Watson' who elucidated the structure of DNA in 1953. This is, without doubt, one of the defining discoveries in science during the 20th century and created the entire industry of biotech. The advantage that Crick and Watson had was that they were both intellectual magpies, coming to DNA after having worked in other areas. On the other hand, they were fortunate in being the first to unravel the structure; colleagues such as Maurice Wilkins and Rosalind "Rosy" Franklin contributed in major ways.

 In 1967 Watson wrote the book "The double helix" that recounts the story of how Watson started work on DNA, how he met Francis Crick, then Wilkins and Franklin, how they progressed and stumbled and progressed and stumbled. It's illuminating to read this book from the perspective of 2025, especially regarding the inter-personal reactions. All three men were condescending, at the least, to Franklin who made the important contribution of the X-ray photographs. 

I suspect that I read "The double helix" while I was at school, or at least at university. I'm fairly sure that we covered DNA, RNA and protein synthesis as part of the A-level biology curriculum, and this was new science - only twenty years after the discovery. But it wasn't until 2004 that I bought a paperback copy of the book (1999 edition) and I have the 2012 annotated version as an e-book. 

It was this book - or rather, the discovery that the book describes - that opened the way for a great deal of my reading in the past few years. Without C&W (not country and western!), there would have been no "The code breaker" by Walter Isaacson, telling the story of Jennifer Doudna and her work on the CRISPR system of gene editing, or Doudna's own book, "A crack in creation", or any of the biochemistry books by Nick Lane that deal in part with how life could have started and how RNA could have been built initially. I also read a biography of Rosalind Franklin whose name escapes me at the moment. Doudna didn't have good words for Watson who had become controversial in later life.

Gene editing was a common topic in science fiction - fiction that later became facr - as in Robert Silverberg's "Up the line", for example.

An obituary can be read here.



This day in blog history:

Blog #Date TitleTags
42709/11/2011Feeling the pressureMBA, Finance
118509/11/2018Improving a solutionProgramming, Priority tips
143909/11/2021The department of bright ideasProgramming, Problem solving
185409/11/2024Beneath a scarlet skyLiterature, Italy

Saturday, November 08, 2025

Implementing CP Prolog - 7

Calculating the factorial function is the poster boy of recursion. It's very easy to define the factorial of a positive number: if the number is 0 then the factorial is 1, else it's the number itself times the value of the factorial of the number less 1. In other words, the factorial of 1 is 1 * factorial (1 - 1) = 1, and the factorial of 2 is 2. More interestingly, the factorial of 3 is 6 (because that's 3 times 2), and the factorial of 4 is 24 (because that's 4 times 6). This can easily be defined in an imperative language such as Pascal as follows.

function factorial (n: integer): integer; begin if n = 0 then factorial:= 1 else factorial:= n * factorial (n - 1) end;

So simple yet so bad, as calculating the factorial of a number such as 10 will cause a great deal of stack space to be used because of the recursion. An iterative calculation is just as simple and executes with a constant and minimal amount of stack space, as well as being faster.

function factorial (n: integer): integer; var i: integer; begin result:= 1; for i:= 1 to n do result:= result * i; end;

But what does the factorial function look like in Prolog?

factorial (0, 1). factorial (N, F):- N > 0, N1 is N - 1, factorial (N1, F1), F is N * F1.

This matches the initial description that I gave. The factorial of 0 is 1, otherwise assuming that N is greater than 0, the result (F) comes from multiplying the number itself (N) by the factorial of N - 1 (F). The rule looks a bit strange but that's because of the syntax that Prolog requires. 

Last week's installment had the interpreter solving several goals where the final goal included an assignment ('is'), multiplication and addition. A great deal of work was spent on getting that 'is' functor to do what it was intended to do. Calculating the factorial depended on that 'is' functor as it is called twice on every invocation. It turns out that it wasn't correct. 

Before I could tackle the factorial, I wanted to ensure that the 'add' 1 function that I added a few weeks ago still worked. It didn't, so first we had to spend no small amount of time getting this to work again. I knew that 'add' would be a goal on the path of getting the factorial to work.

Let us say that I spent several exceedingly frustrating hours yesterday and today trying to get my interpreter to calculate the factorial. I would run a test and show CoPilot the results. 'Add this', it would return. So I added something more and sometimes it would improve the situation but more often that not would make no change. There are parts of the interpreter (especially handling the 'is' functor) that went through several changes. Sometimes the changes would make things worse, meaning that I had to restore code from previous versions. At one stage, CoPilot said 'do A', then when that didn't work, 'do B', then 'do A'. Exceedingly frustrating. It completely ruined the 'resolve' function that lies at the heart of Prolog, so I had to restore this from last week's code (I back everything up religiously).

CoPilot of course was not running blind. I would run diagnostics and sometimes had enough understanding to see what the current problem was. A key breakthrough was made by me when we saw that that a specific block of code from CP (which had already been tweaked and improved on a previous version) was not doing something that it was supposed to do. That insight almost completely solved the problem. Finally, after having added and removed the same lines time and time again, CP said that You need to ensure that after the rule body succeeds, the rule head variables are unified with the goal arguments. (Does that make any sense? Why wasn't this suggested before?) This was the final piece in solving the puzzle. Now my interpreter can calculate factorials.

In getting this far, I had to take out several of the instrinsic functions that were added last week, so now these have to be restored, and there's no guarantee that the code that worked a week ago will work now because several aspects have been changed and tightened up. It seems that 'maintenance work' (i.e. restoring functions that had been removed to make things clearer) takes more time than actual development work.

I have two more goals to solve (deliberate Prolog speak), although these are not concerned with the external functionality of the interpreter. I want to implement 'garbage collection': in the course of a single query, a certain amount of memory is allocated which is not needed after the query completes and so should be released (and the calculation of factorial (10) will require much more memory than factorial (1)). I know that this doesn't make much difference when one has megabytes of memory, but it's good housekeeping.

In a similar manner, there's a small improvement that should be made in handling rules. Going back to the original family tree example2, there were various functors used, like 'parent', 'male', 'female', 'father', 'mother' and 'grandparent'. If one is trying to find a fact that matches 'male', for example, there should be no need to iterate over all the 17 facts/rules in the database, but only those where the functor is 'male'. I know that this is done by means of a symbol table, where each functor is assigned an index: finding a suitable fact/rule can be done by comparing two index numbers as opposed to invoking the 'expensive' resolution functionality. I suspect that this will be relatively simple, but what seems to be simple is often complicated.

Internal links
[1] 2023
[2] 2014



This day in blog history:

Blog #Date TitleTags
64508/11/2013Sorting in Excel via Delphi automationProgramming, Delphi, Excel, Office automation
90008/11/2015Conceptual changeDBA
90108/11/2015900 blogsMeta-blogging
143808/11/2021DBA updateDBA
155108/11/2022First rain of the yearWeather
185308/11/2024New CPAP maskCPAP

Thursday, November 06, 2025

New smart watch

Several years ago, I posted about a smart watch that I received for free from my health fund by virtue of walking a lot and accumulating points in their application. I had seen that my daughter has some form of smart watch (not from the health fund) and this planted a seed in my mind that maybe it was time again to try with a smart watch. Maybe they've got smarter over the past five years. 

The watch required 69,900 points (and an extra 60 NIS) for purchase (a site selling this watch in Israel has it at 423 NIS, discounted from 499 NIS); a week in which I achieve my walking goal gives me 1,500 points, and most days I earn an extra 30 points, so a normal week will see my point count increase by about 1,600 points. That means that I've been saving for about 44 weeks or ten months. At the moment my weekly goal is 'only' 58,500 steps a week, or 8,357 steps a day. There are days when I barely make this target and days when I far exceed it. As the purpose of the application is to encourage exercise, the weekly total is supposed to increase, but it's been about the same for the past few weeks. I could easily walk more, but if I do this, then the target will increase and then maybe I won't be able to make the weekly target. So I'm gaming the system at the moment by trying to make the daily target; normally once a week I will walk far more, so then on Saturdays I try to walk as little as possible in order not to increase the target. 

Once I received the watch, the usual trauma started: how do I charge it? How do I make it work? The charger cable comes with a USB plug at one end and a magnetic strip at the other that connects to the watch (but only one way around). After charging the watch for a few hours, I turned it on and tried to figure out how to use it. The instructions were mainly a set of pictures, too small to be useful, so they didn't help. I contacted my daughter, but she has a Samsung watch and so she could only give advice: the clock on the watch should synchronise once I run an application on my phone.

The booklet came with a QR code, but when I initially scanned it, an app for an online bank started installing. Reading the fine print a bit closer, it talked about installing 'Smart life', but this turned out to be an app that controls smart devices within the home. Eventually I scanned the QR code again and this time another app called 'Smart life' (or rather, 'Oplayer smart life') from Synergy Innovations started to install. It wasn't clear at first how it would connect to the watch: I could see the bluetooth icon on the watch but when I turned on the phone's bluteooth and started scanning, no watch could be found. Eventually the app connected to the watch and since then everything that appears on the watch also appears on the phone.

What does the watch do? Apart from telling the time (useful at night) and measuring steps, it also measures my pulse and oxygen saturation. Supposedly it also measures something to do with sleep, presumably light and deep sleep. It might also measure a few more things, such as swimming, but that can wait for several months.

How accurately does the watch measure steps and pulse? At the moment of writing, Samsung health says that I have walked 3,532 steps today, whereas the watch says 3,427. My pulse last night as measured by the sphygmomanometer was 80 bpm whereas the watch said 87 bpm. There was a similar gap this morning. Intriguingly, I can see a graph of readings starting at midnight although I can't see what each point represents. At midnight and until I got up at 5:30 am, the pulse was low; it then increased just before 6 am which is when I was walking the dog, then decreased again. 

The screenshot from the phone app gives an indication of the pulse ("heart health") without actually showing the values. Similarly with sleep: the app only started measuring at midnight (and yesterday there seems to be no sleep at all) so I 'slept' only 5 hrs 12 minuts (which is approximately correct if it's only from midnight); I think that light sleep was from 00:00 until 00:17 (17 minutes), then again from 01:35 until 02:53 (78 minutes), but after that there aren't enough times given that line up with the start or end of periods. To me, it looks about 50% each. 

I have just discovered that if I press on that graph (on the phone), I can get details for a day, a week or a month. So I spent 2 hours and 4 minutes in deep sleep and 3 hours 8 minutes in light sleep. I was in deep sleep for 39% of the time measured, which is considered normal, whereas I was in light sleep for 61% which apparently is high. That's slightly confusing: the deep sleep should be considered low if the light sleep is high. But where's my sleep from 9 pm the evening before? 

I can also see that my pulse decreased from 61 bpm to about 50 bpm during the night. Pressing on the pulse segment and requesting 'more data' gives me a reading for about once every half hour, including yesterday evening. Apparently my pulse was at 146 bpm yesterday evening at 20:26; as I was watching television at the time, one can only assume that my pulse increased because I was so digusted at the news. At 20:58 the pulse was back down to 68 bpm and decreased to 51 bpm at 22:34.

This is all very interesting, but not too valuable if the data don't match those measured in other ways (and at the moment, they don't!). On the other hand it can give indications and trends. I hope that the problem with measuring sleep will sort itself out.

Internal links
[1] 1419



This day in blog history:

Blog #Date TitleTags
5906/11/2006ShoppingVan der Graaf Generator, Peter Hammill, Musical instruments, Keyboard
21506/11/2009Eliza Carthy, "Red"Morse, Eliza Carthy
30106/11/2010Trains and mealsCooking, Trains, Slow cooker
108806/11/2017Forty years agoPersonal, Sandy Denny, Heron
118306/11/2018Intermediate thesis submitted!DBA
143606/11/2021Opening Word from a thread and displaying a fileProgramming, Delphi, Threads
155006/11/2022Appearances in my school's ChroniclesPersonal, Bristol Grammar School
185106/11/2024More dopamineProgramming, Blog manager program
185206/11/2024Guy Fawkes nightIsrael, Yuval No'ach Harari

Tuesday, November 04, 2025

Squamous Cell Carcinoma (SCC)

Today I had the operation for removal of a growth on my neck to which I referred1 six weeks ago. We arrived at the hospital far too early, and would have arrived even earlier had we taken advantage of a short cut that I wasn't sure about (the 'new' road 16 that surfaces very near the hospital). It was like waiting for an airplane: first we waited for the check-in counter to open, then I checked in, then after another wait, the plastic surgery clinic opened and a nurse checked me, then we waited some more then eventually another nurse called me into surgery.

The operation - if one can call it that - was very similar to all those that I've had before, although this time it seemed to be more involved in that a larger area of skin seemed to be removed. There was cauterisation (creating the smell of burnt flesh) and self-dissolving stitches, followed by the application of a relatively large bandage.

The tissue that was excised was sent to a laboratory within the hospital, and after a wait of nearly two hours, the result arrived: a squamous cell carcinoma had been removed. What's the difference between BCC and SCC?

  • Basal Cell Carcinoma (BCC) arises from basal cells located in the lower part of the epidermis.
  • Squamous Cell Carcinoma (SCC) develops from squamous cells found in the upper layers of the epidermis.

To you and me they're more or less the same. At the moment it's difficult to tilt my head because of the huge bandage, but tomorrow it will come off and life will go back to normal.

Internal links
[1] 2001



This day in blog history:

Blog #Date TitleTags
21404/11/2009HejiraRandy Newman, Joni Mitchell
42504/11/2011Coffee addicts (Millennium trilogy)Films, Meg Ryan, Steig Larsson, Time traveler's wife
42604/11/2011User defined menusProgramming, Delphi
143404/11/2021Sleeping deeplyCPAP, MP3, Binaural beats, Sleep
154804/11/2022Redesigning the 'blogs' programProgramming, Blog manager program

Saturday, November 01, 2025

More apneic events

I wrote1 ten days ago about the high number of apneic events that my CPAP machine had been recording. I had two hypotheses for these high numbers: the mask is reaching the end of its life, and the lack of amitryptiline. 

Well, today I reviewed the numbers for the past week, and whilst they're not quite as bad as those from early October, they're still far too high. I've been taking amitryptiline every night, so I can see that if it did have an effect, it's only minor. I'm going to order the new mask tomorrow, as we're now in November, and last year's mask was bought in November: I'm entitled to a free mask once a year. I don't think that the mask will have much of an effect either.

A third hypothesis occurs to me: maybe my weight loss is effecting the apnea. Since writing that blog, I've dropped another half kilo from my weight. I looked at my records and saw that I was nearly the same weight in May 2023: I had lost weight in a similar manner to that of today (although I soon started putting it back on), but there seems to have been no effect on the number of apneic events. As I suffer from central sleep apnea, which is caused by the brain failing to send proper signals to the muscles that control breathing, it seems unlikely that weight would have any effect.

I shall try to find a 'sleep doctor' whom I can consult.

Internal links
[1] 2024



This day in blog history:

Blog #Date TitleTags
77201/11/2014Undoing textual changesProgramming
154501/11/2022Big cloudWeather

Friday, October 31, 2025

Implementing CP Prolog - 6

Last week I presented the rule

time_in_minutes(TimeAtom, Minutes) :- atomic_list_concat(TimeAtom, ':', [HStr, MStr]), atom_number(HStr, H), atom_number(MStr, M), Minutes is H * 60 + M.

I ran out of time implementing atomic_list_concat (which is, along with atom_number, a standard Prolog predicate and not something that CoPilot invented); it wasn't as it appears above, but rather in the form atomic_list_concat ([H|T], ':', TimeAtom). I thought that I would start off this week by putting the arguments in the more logical order, but unfortunately this caused problems with the implementation of this predicate as it currently stood. So I had to work on this, finally getting the predicate to work both ways.

I thought it would be instructive to add logging to the goal resolver so that one could see what the current goal to be resolved is. This is helpful to see where the rule was getting stuck. I used some standard code of mine for doing this.

The standard predicate 'atom_number' should have been relatively simple to implement, but the initial code that CoPilot presented was insufficient. It hadn't considered the possibility of both arguments being variables, and in any case, CoPilot had neglected to resolve the arguments first before dealing with them. Eventually we got the code to work. In the goal resolver, first one must handle the current goal then if that succeeds, a recursive call must be made to resolve the remainder of the goals.

What does this mean? The initial goal is time_in_minutes (03:30, X). This gets replaced by the four clauses in the rule's body. If the goal atomic_list_concat succeeds, then a recursive call must be made with the three remaining goals, and if the next goal succeeds (atomic_number), then a recursive call must be made with the two remaining goals. This is something that I had missed in previous weeks for the comparison operators: I was assuming that these were the final goal to be solved, but in reality they could appear anywhere.

Once atom_number was working correctly, it was time to handle the arithmetic expession. Again, I had solved simple expressions such as 3 + 4, but here a recursive evaluation function was required. Fascinating code. I should point out here that depite all the sophistication of CoPilot, it made a rookie mistake here: one can't have a case statement where the value being evaluated is a string. This was easily corrected but just goes to show that the initial code is not always correct.

Finally the entire rule and goals were handled correctly, so that querying time_in_minutes (03:30, X) gave the solution X = 210. 

Before leaving this level of the interpreter, I wanted to refactor the common logic from ResolveGoals that I had been adding to the intrinsic functions that makes the recursive call to handle the remaining goals. This was partially successful: it broke down in places because the signature of AtomicListConcat, namely (args: PTermArray; var env: TEnvironment): boolean is not the same as that of AtomNumber, (atomTerm, numberTerm: PTerm; var env: TEnvironment):boolean. It was more successful with the comparison operators, although these too required helper functions.

What's next? First, I want to expand the 'flight finder' database - this requires the time_in_minutes rule to ensure that the passenger gets to the airport at least three hours before a flight is due to leave. But after that? I don't know. If I read some Prolog texts over the next week then maybe I will come across something that I would like to implement. But as I wrote a few weeks ago, I suspect that I am much more interested in learning how to write the interpreter than actually use it.



This day in blog history:

Blog #Date TitleTags
108631/10/2017Tom PaxtonPersonal
127031/10/2019Coincidence or deliberate reference?John Le Carre, Non-fiction books
154431/10/2022Extending cooking length for chickenCooking, Ninja grill

Thursday, October 30, 2025

Counting beats with Van der Graaf Generator (5) - After the flood

I've never particularly liked this song that closes the first "real" VdGG album, 'The least we can do is wave to each other'. I've always found it grandiose and striving too much for effect: two features that normally don't appear in other songs of theirs. Last night, the song came on my headphones whilst walking the dog, and this time I paid attention to the various time signatures being played. The results are more than interesting.

The opening riff is in 104 - the guitar strum is "on the 10". The next part, with the lyric "Clouds have gathered" has one bar in 44 followed by one in 64. I didn't realise this last night, but writing this now makes me realise that these lines could also be viewed as being in 104. The pace then slows for more bars in 44. The "chorus" ('And when the water falls again') is clearly in 44.

At the 4:50 minute mark, a humungous riff starts to be played that repeats several times: this is in 124 - I think it's correct to write it this way and not as three bars of 44 as every note is emphasized equally. If I remember correctly, each note is only played once during each sequence making this an example of "12 tone serial music". This carries on for another minute and a half (probably too much) before a simple passage with (initially) only acoustic guitar and bass: this is in standard 44, until the word 'Annililation' is cried out (as someone wrote, like a crazed Dalek) over the 124 rhythm that returns for a few elongated bars.

From hereon, we're back into 44 for the extended coda. The bass tends to play syncopated beats on the "and" of beat 4 of the previous bar, i.e. anticipating the first beat by a quaver.


This day in blog history:

Blog #Date TitleTags
42130/10/2011Sir Jimmy SavileObituary, RIP
42230/10/2011Succulent SundayCooking, Slow cooker
77130/10/2014Another good ideaERP
118130/10/2018Losing an old friend, gaining a new oneComputer
143330/10/2021Binaural beats and deep sleepCPAP, MP3, The brain, Binaural beats, Sleep
154330/10/2022Adding more old blog entriesProgramming, Meta-blogging, Blog manager program

Saturday, October 25, 2025

Implementing CP Prolog - 5

The goal of this week's installment is to parse, print and solve correctly a query such as
?- time_in_minutes (05:40, X), according to the rule shown below. If everything goes correctly, then X = 340 should be the result. 5 hours after midnight is 300 minutes, along with another 40 minutes. Here's the rule:
time_in_minutes(TimeAtom, Minutes) :- atomic_list_concat(TimeAtom, ':', [HStr, MStr]), atom_number(HStr, H), atom_number(MStr, M), Minutes is H * 60 + M.

The code from last week is line-orientated: it reads each line as a separate fact, rule or query, deleting the final full stop (in fact, frequently I would omit the full stop when typing as it didn't do anything). In order to read the above correctly, each line has to be concatenated in a buffer until a full stop is read at the end of a line (a full stop in the middle of a line should be ignored at this stage). The fix for this had to be implemented in two places: in the main loop that reads user input, and in the procedure that loads a text file.

Parsing the rule was more difficult. The head (i.e. time_in_minutes) was read correctly, but when I tried to query using this predicate, the first and only subgoal that appeared had the 'is' predicate. Obviously all the subgoals until the final one were being ignored. I tracked this down to a location where the presence of an inline operator in the line is checked before dividing the line into parts. This is a direct result of the queries that I was writing last week, such as add (Operand1, Operand2, Result):- Result is Operand1 + Operand2. I hadn't considered the possibility that the rule might have several clauses, where not all of them had inline operators. Once I found the problem, solving it was fairly simple: first divide the body (i.e. list of subclauses) into separate clauses, then handle each clause on its own. 

So the rule's body has four clauses, where the final clause is a 'triple whammy': three infix operators. This should be translated into the form: is (Minutes, * (H, + (60, M))). Now this complex term is built correctly.

The same problem of assuming that if the rule's body has an infix operator occurred in the printing (or 'showing') code. Again, each clause should be handled on its own, where the final clause gets passed to the recursive PrintInline function. Now the rule is printed correctly.

The rule assumes two intrinsic predicates, atomic_list_concat and atom_number. The latter is probably easier to understand; if the variable (first argument) is a number, return its value in the second argument. So if HStr has the atomic value '05' then H should be 5. The first predicate splits an atom whose value is <something> colon <something> (e.g. 05:40) into a list, where the first entry in the list will be 05 and the second 40. It's just as well that I did some work on lists, although I don't know whether it will be sufficient for this case.

For some unknown reason, I had modified the first clause in the rule and all my debugging was on this one clause.

?- atomic_list_concat ([H|M], ':', 04:30).

Maybe this is what I had originally and CoPilot changed it. Whatever. This query required all kinds of changes and I spent hours on it. Parsing and printing had to be changed to allow this new kind of list, [H|M]. Resolution also required changes. Despite the myriad changes, for quite some time the solution to the above query was H=H, M=M, which is a clear sign that the environment is not being updated somewhere. CoPilot says the environment should be passed as a VAR variable everywhere so I changed this wherever necessary. Now H= 04 and M = 30. Halleluya!

I don't have the necessary strength of will to implement the predicate 'atom_number' at the moment. I'm fairly sure that it's called something else in other implementations. At the moment, it won't actually do much - the term's name '30' will be replaced by '30' because name is a string field, but '04' should be replaced by '4'.



This day in blog history:

Blog #Date TitleTags
1925/10/2005The Time Traveler's WifeLiterature, Robert Silverberg, Time traveler's wife
21225/10/2009The importance of building indexesProgramming, SQL, Firebird
89625/10/2015The end of summer timePersonal
98425/10/2016What I did on my holidaysDBA, MIDI, Steig Larsson
143125/10/2021Blind date (new song)Song writing, Home recording
153925/10/2022Roland J-6 minisynth: a serious musical instrument or an expensive toy?Musical instruments, Roland J-6