Logical Loop

Picat apostate? Concerning loops, I guess SWI-Prolog has:

foreach(:Generator, :Goal)
https://www.swi-prolog.org/pldoc/man?predicate=foreach/2

But I dont know whether some other loop construct are supported in SWI-Prolog, like Logical Loops, and you might indeed be accustomed to more loop constructs from Picat. For list comprehension there might be indeed only Picat that has something more advanced.

Ideally for list comprehension we would maybe adapt some Logical Loops ideas. I never tried. But similar like Logical Loops, some list comprehension can be translated into auxiliary predicates by using the same generator building blocks.

@j4n_bur53 :slight_smile: Absolutely not. I still think in Picat (which you see in my SWI-Prolog code).

But there are things in Prolog that I do miss in Picat, e.g. DCG, assert/retract (since Old School Prolog books use these), some of the useful/cool/fun/experimental packages in SWI-Prolog etc,

Regarding loops, I do mean more like Logical Loops. I tend to use ```findall/3'a lot, but it’s still not the same.

Yes, that would be really great!

@j4n_bur53 That’s an interesting idea.

The first test is to label the generated list L, but it don’t work as expected.

?- phrase(foreach((between(1,4,N),X#>N),[X]),L),label(L).
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:   [16] throw(error(instantiation_error,_21190))
ERROR:   [13] clpfd:'__aux_maplist/2_must_be_finite_fdvar+0'([_21224,_21230|...]) at /usr/lib/swi-prolog/library/clp/clpfd.pl:1745
ERROR:   [12] clpfd:labeling([],[_21268,_21274|...]) at /usr/lib/swi-prolog/library/clp/clpfd.pl:1748
ERROR:    [9] <user>

Can you see a solution for this?

Ah, one must give X a domain first. So this works:

?- X in 0..10, phrase(foreach((between(1,4,N),X#>N),[X]),L),flatten([X,L], Vars),labeling([],Vars).
X = 0,
L = [2, 3, 4, 5],
Vars = [0, 2, 3, 4, 5] ;
X = 0,
L = [2, 3, 4, 6],
Vars = [0, 2, 3, 4, 6] ;
X = 0,
L = [2, 3, 4, 7],
Vars = [0, 2, 3, 4, 7] ;
...

I’ll play with this more…

I’m at SWI-Prolog (threaded, 64 bits, version 8.1.13).

When I test this in SWI-Prolog 8.1.13, I got this error:

ERROR: Unknown procedure: (^)/4
ERROR: In:
ERROR:   [15] ^(_58686,({...},[_58706]),_58690,_58692)
ERROR:   [14] '$dcg':call_dcg(user:_58752^ ...,_58740,_58742) at /usr/lib/swi-prolog/boot/dcg.pl:368
ERROR:   [12] dcg_high_order:emit_list([v(1),...|...],v(_58816),v(_58820),user:_58830^ ...,dcg_high_order:[],_58794,[]) at /usr/lib/swi-prolog/library/dcg/high_order.pl:181
ERROR:    [9] <user>

I assume you meant SWI-Prolog with library(dcg/high_order).

Is this the latest Logical Loops code?


(The link in the paper was dead and Google search was not very helpful; I first found this page, then searched for some parts of the source code to find @jan’s code.

1 Like

Partially answering my own question:

My guess is that it isn’t the latest version because there’s this in Schimpf’s version and @Jan’s version seems to be dated 2015:
% Changes: small change to metacalled code 2018.

Using archive.org, It seems that this was the change:

$ diff -U 3  wayback-loops.pl.txt loops.pl.txt
@@ -5,6 +5,7 @@
 %
 % Author: Joachim Schimpf, IC-Parc, Imperial College, London
 % Copyright (C) Imperial College London and Parc Technologies 1997-2002
+% Changes: small change to metacalled code 2018.
 %
 % This source code is provided "as is" without any warranty express or
 % implied, including but not limited to the warranty of non-infringement
@@ -19,16 +20,15 @@
 % Definition for metacall

 (Specs do PredTemplate) :-
-       get_specs(Specs, Firsts, BaseHead, PreGoals, RecHead, AuxGoals, RecCall),
+       get_specs(Specs, Firsts, Lasts, PreGoals, RecHead, AuxGoals, RecCall),
        !,
        call(PreGoals),
+       copy_term(Lasts, BaseHead),
        do_loop(Firsts, body(RecHead,(AuxGoals,PredTemplate),RecCall), BaseHead).
 (_Specs do _PredTemplate) :-
        write('Error in do-loop specifiers'), nl.

-    do_loop(Args, _BodyTemplate, BaseHead) :-
-       copy_term(BaseHead, Copy),
-       Copy = Args, true, !.
+    do_loop(Args, _BodyTemplate, Args) :- !.
     do_loop(Args, BodyTemplate, BaseHead) :-
        copy_term(BodyTemplate, Copy),
        Copy = body(Args, Goal, RecArgs),
@@ -198,3 +198,4 @@
     compute_stop(From, To, Step, Stop, Goal) :- Step < 0, !,
        Goal = (Dist is max(From-To-Step,0),
                Stop is From - Dist + (Dist mod Step)).
1 Like

@peter.ludemann

Thanks for the (reminder of the) links, Peter. I used these Logical Loops a lot when testing ECLiPSe CLP (http://hakank.org/eclipse/ ) and SICStus Prolog (http://hakank.org/sicstus/ ).

Joachim Schimpf’s paper “Logical Loops” is available from the ECLiPSe page you linked to (http://eclipseclp.org/software/loops/index.html). Here’s the PDF version: http://eclipseclp.org/reports/loops.pdf

Just as a proof-of-concept, I installed Jan W’s version of loop.pl (https://github.com/JanWielemaker/logical-loops) and tested it on some of my ECLiPSe Prolog predicates for Euler problem 1: http://hakank.org/swi_prolog/euler1_loop.pl

Here’s the code

%% From https://github.com/JanWielemaker/logical-loops
:- use_module("/home/hakank/swi_prolog/git/jan_wielemaker/logical-loops/loops.pl").

go :-
        L = [
             euler1a,
             euler1b,
             euler1c,
             euler1d
            ],
        ( foreach(P, L) do
        writeln(P),
          time(P),
          nl
        ),
        nl.

euler1a :-
        numlist(1,999,L),
        filter(div3_or_5, L, Includes),
        sumlist(Includes, SumList),
        writeln(SumList).


euler1b :-
        ( for(N,1,999), 
          fromto(L,Out,In,[]) 
        do 
        ( M is N mod 3 ; M is N mod 5), 
          M =:= 0 -> Out = [N|In] ; Out = In 
        ), 
        sumlist(L,S),
        writeln(S).

euler1c :-
        ( for(I,1,999),
          fromto(0,In,Out,Sum) do
        div3_or_5(I) -> 
          Out is In + I
        ;
          Out = In
        ),
        writeln(Sum).

euler1d :-
        (
        for( I,1,999),
        fromto(0,In,Out,Sum) do
            M3 is I mod 3,
            M5 is I mod 5,
            (M3 == 0 ; M5 == 0) -> 
            Out is In + I
        ;
            Out = In
        ),
        writeln(Sum).



div3_or_5(N) :-
        0 is N mod 3 ;  0 is N mod 5.

applyP(P, Xs) :- Query =.. [P,Xs], Query.

filter(P, List1, List2) :-
        ( foreach(X,List1), 
          fromto(List2,Out,In,[]), 
          param(P) do 
        applyP(P, X) -> Out = [X|In] ; Out=In
        ).

The output is

?- make,go.
euler1a
233168
% 8,804 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 14555266 Lips)

euler1b
233168
% 6,465 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 16116889 Lips)

euler1c
233168
% 5,330 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 16417984 Lips)

euler1d
233168
% 3,465 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 8829330 Lips)

true.

Note: I got a warning which that is not occurrent when I run the program in ECLiPSe :

Warning: /home/hakank/swi_prolog/me/euler1_loop.pl:88:
Warning:    do/2: inconsistent parameter declaration
Warning:    	Shared but not declared: List1 and List2

It would be great if this would be incorporated in SWI-Prolog.

(Hmm, for some reason, I never published my Project Euler programs for ECLiPSe CLP. This I must fix…
Later: And now that’s fixed: http://hakank.org/eclipse/ and https://github.com/hakank/hakank/tree/master/eclipse_clp/ )

This is, as far as I recall, the result of porting and improving SWI-Prolog integration based on the code associated with Joachim’s paper. As I recall, I ran some tests, got into a problem and contacted Joachim. He told me the current version in ECLiPSe contains quite a few improvements compared to the paper.

I think the proper way forward is to that the ECLiPSe code and port it. That isn’t totally trivial as it relies on a lot of low-level ECLiPSe stuff (in my memory) that is quite different from the low level stuff in SWI-Prolog.

Again, this is all from my memory. If someone wants to pick this up and realise a proper working and integrated implementation I’m happy to add it to the standard library. Most likely the is an extensive test set with ECLiPSe and I can help integrating.

2 Likes