[comp.lang.prolog] loops in prolog

vincel@fs0.ee.ubc.ca (vincent li) (02/09/90)

Question: how would you code the following loop in (Edingburgh)
Prolog?

loop:
   action_1;
   IF condition_1 THEN
     action_2;
     goto loop
  END IF
  action_3;

Granted this is bad code, but the idea is to perform action_1 and
action_2 within the loop. action_1 would change some data, say, and
condition_1 would check the result. If condition_1 is satisfied, then
action_2 is performed and stays in the loop, otherwise rest of the
program is continued with action_3 WITHOUT doing action_2.

The code I came up with looks something like this:

loop :-
   action_1,
   (condition_1 -> action_2,loop),
   action_3.

This works, but the recursive call eats up memory and I get out of heap
errors. Is there some other way of doing this? A fail driven loop seems
an obvious alternative, but action_1 is a proven-once predicate (ie.
fails on backtrack). So, this doesn't seem to work:

loop :- action_1,condition_1,action_2,fail.
loop :- action_3.

Thanks in advance for any suggestions.
Vince.
--------------------------------------------
vincel@ee.ubc.ca
vinceli@triumfcl.bitnet
vincent@graham.uucp
.

ok@goanna.oz.au (Richard O'keefe) (02/09/90)

In article <1032@fs1.ee.ubc.ca>, vincel@fs0.ee.ubc.ca (vincent li) writes:

> Question: how would you code the following loop in (Edingburgh) Prolog?

> loop:
>    action_1;
>    IF condition_1 THEN action_2; goto loop END IF;
>   action_3;

The answer is dead simple.
	loop :-
		action_1,
		(   condition_1 ->
		    action_2,
		    loop
		;/* ~condition_1 */
		    action_3
		).
> The code I came up with looks something like this:

> loop :-
>    action_1,
>    (condition_1 -> action_2,loop),
>    action_3.

> This works, but the recursive call eats up memory and I get out of heap

I have some bad news for you.  It DOESN'T work.  What this says is
to do action_3 WHETHER OR NOT condition_1 succeeds, and that's NOT
what the original sketch said!  You begin to convince me that Dijkstra
was right when he omitted the one-armed-IF-bandit from his notation.

ken@aiai.ed.ac.uk (Ken Johnson) (02/11/90)

> Question: how would you code the following loop in (Edingburgh)
> Prolog?
>
>loop:
>   action_1;
>   IF condition_1 THEN
>     action_2;
>     goto loop
>  END IF
>  action_3;

Answer

loop :-
	action1,
	loop1.

loop1 :-
	condition1,
	!,
	action2,
	loop.

loop1 :-
	action3.

Any other homework you want done, just post it. 

-- 
Ken Johnson, AI Applications Institute, 80 South Bridge, Edinburgh EH1 1HN
E-mail ken@aiai.ed.ac.uk, phone 031-225 4464 extension 212
`I have read your article, Mr Johnson, and I am no wiser now than when I
started'.  -- `Possibly not, sir, but far better informed.'

stefan@hpbbi4.HP.COM (#Stefan Bachert) (02/27/90)

Hello,

Try

loop:-
	repeat,
	action_1,
	(not condition_1;action_2,fail),
	action_3.


You must take care of backtracking. Use a predicate
like 
	once_true(X):-call(X),!.
	once_true(_).

for calling action_? . 

I hope this will help you


Stefan Bachert

ok@goanna.oz.au (Richard O'keefe) (02/28/90)

In article <470002@hpbbi4.HP.COM>, stefan@hpbbi4.HP.COM (#Stefan Bachert) writes:
> loop:-
> 	repeat,
> 	action_1,
> 	(not condition_1;action_2,fail),
> 	action_3.

BEWARE.  A 'repeat" loop should always have its cut in the same clause.
The cut is just as much part of the loop structure as the 'repeat'.
It is *extremely* confusing for human readers if you put the cut in
some other predicate (rather like putting the 'end loop' of an Ada
loop in a different procedure).

A tip:  if you have some rather scrambled control structure it may
be useful to first try to write down a regular expression that
describes the possible sequences of tests and actions, and use the
regular expression as the basis of your code.

In this example, I am guessing that the intention is
	action_1 (condition_1 action_2 action_1)* ~condition_1 action_3
which naturally leads to the structure

    p :-
	action_1,
	p_loop.

    p_loop :-
	(   condition_1 -> action_2, action_1, p_loop
	;/* ~condition_1*/ action_3
	).

All done by kindness.

stefan@hpbbi4.HP.COM (#Stefan Bachert) (03/01/90)

Hello,

you are right. A cut before action_3 will clarify the situation.
I am not sure that you get me right with the idea of 
once_true. So I repeat it here

	once_true(X):-call(X),!.	% predicate X is determinated
	once_true(_).			% and always true

	loop:-
		repeat,
		once_true(action_1),
		(not condition;once_true(action_2),fail),!,
		once_true(action_3).

You proposal will still eat up the stack. I think the basenote
author has to judge what proposal fit for his problem.

Stefan Bachert

PS: I don't like ->/2 because of its dubious definition.

ok@goanna.oz.au (Richard O'keefe) (03/02/90)

In article <470003@hpbbi4.HP.COM>, stefan@hpbbi4.HP.COM (#Stefan Bachert) writes:
> I am not sure that you get me right with the idea of 
> once_true. So I repeat it here

> 	once_true(X):-call(X),!.	% predicate X is determinated
> 	once_true(_).			% and always true

I am *sick* of seeing people reinvent once/1 and giving it new names.
It is traditionally a library predicate in Edinburgh Prologs.

I've even *more* sick of people thinking it is a good idea.
If X is supposed to be determinate, then *WRITE* X so that it *IS*
determinate, don't patch it up in the calls.  When I see a program
using once/1 I am willing to bet that it is badly written in other
ways too.

> 	loop:-
> 		repeat,
> 		once_true(action_1),
> 		(not condition  ;  once_true(action_2),fail),
		!,
> 		once_true(action_3).

The actions should be written so that they are ALREADY determinate,
if that is truly what is intended.  The original author really did
not make it clear.  

> Your proposal will still eat up the stack.

Not so.  Stack space is reclaimed on failure or on determinate exit,
and tail calls don't take stack space in a modern Prolog.  Recall
that the recursive form of the loop was
	p :- action_1, p_loop.
	p_loop :- ( condition -> action_2, action_1, p_loop ; action_3 ).
If action_1 and action_2 are determinate, then this loop does *NOT*
"eat up the stack" in a decent Prolog system.  (Note that in the 'repeat'
form of the loop action_1 had better not fail, otherwise there's a nasty
liitle infinite loop there.  The recursive version has no such problem.)

> PS: I don't like ->/2 because of its dubious definition.

*WHAT* dubious definition?  The if->then;else construct was defined
quite clearly in the DEC-10 Prolog manual, as far back as 1979:

	( P -> Q ; R ) is like call(( P, !, Q ; R )) except that
	it is transparent to cuts in Q and R

	( P -> Q ) is like call(( P, !, Q )) except that
	it is transparent to cuts in Q

How is this any more dubious than once/1?

stefan@hpbbi4.HP.COM (#Stefan Bachert) (03/05/90)

>> 	once_true(X):-call(X),!.	% predicate X is determinated
>> 	once_true(_).			% and always true

>I am *sick* of seeing people reinvent once/1 and giving it new names.
>It is traditionally a library predicate in Edinburgh Prologs.

once/1 is defined as

	once(X):-call(X),!.

and that for it can be FAILED.

>I've even *more* sick of people thinking it is a good idea.
>If X is supposed to be determinate, then *WRITE* X so that it *IS*
>determinate, don't patch it up in the calls.  When I see a program
>using once/1 I am willing to bet that it is badly written in other
>ways too.

In your last comment you talked about clearness of programs.
Using once or once_true will clarify the situation. Your proposal
will do it not! 

>> Your proposal will still eat up the stack.

>Not so.  Stack space is reclaimed on failure or on determinate exit,
>and tail calls don't take stack space in a modern Prolog.  Recall
>that the recursive form of the loop was
>	p :- action_1, p_loop.
>	p_loop :- ( condition -> action_2, action_1, p_loop ; action_3 ).
>If action_1 and action_2 are determinate, then this loop does *NOT*
>"eat up the stack" in a decent Prolog system.

If so your proprosal will run just on your special implemention of prolog.
It takes a lot knowledge to detect that p_loop is determinated. And it is not
obvious to the programmers.
My proposal will run in any prolog.

>(Note that in the 'repeat'
>form of the loop action_1 had better not fail, otherwise there's a nasty
>liitle infinite loop there.  The recursive version has no such problem.)

That's why I introduce once_true/1

>> PS: I don't like ->/2 because of its dubious definition.

>*WHAT* dubious definition?  The if->then;else construct was defined
>quite clearly in the DEC-10 Prolog manual, as far back as 1979:

>	( P -> Q ; R ) is like call(( P, !, Q ; R )) except that
>	it is transparent to cuts in Q and R
>	( P -> Q ) is like call(( P, !, Q )) except that
>	it is transparent to cuts in Q

Exactly that. Try to implement -> /2 in Prolog. (? exceptions)
->/2 is more a precompiler directive than a prolog predicate.

Stefan