[comp.lang.prolog] Prolog Prog. Technique Question

tg1e+@andrew.cmu.edu (Timothy R. Gottschalk) (08/13/90)

I took a course that introduced Prolog syntax, but never touched prog.
techniques.  Thus, I am feeling my way through.  I am writing code similiar
to the following structure (in Turbo Prolog V2.0):

  loop(In):-
      check_mouse,
      do_eval(In, Out),
      loop(Out).
  loop(_):-
      do_exit.

The compiler recognizes that it is tail-recursive, but still allocates memory
off the heap during each iteration.  As the loop should only be run when
the mouse button is pressed, the check_mouse succeeds when the button is
pressed, but fails otherwise.  Commenting out the check_mouse or do_eval
call does not necessarily stop the heap allocation, although commenting
out both certainly does (of course).  The easiest solution would be to
make a predicate fail somewhere, but, then, how would I be able to get a
return value?  (Using reference vars or internal dbases seems to make the
program run unacceptably slow).

Tim G.

fore057@canterbury.ac.nz (08/13/90)

In article <kalQPim00WB242VkMS@andrew.cmu.edu>, tg1e+@andrew.cmu.edu (Timothy R. Gottschalk) writes:
> I took a course that introduced Prolog syntax, but never touched prog.
> techniques.  Thus, I am feeling my way through.  I am writing code similiar
> to the following structure (in Turbo Prolog V2.0):
> 
>   loop(In):-
>       check_mouse,
>       do_eval(In, Out),
>       loop(Out).
>   loop(_):-
>       do_exit.
> 
> The compiler recognizes that it is tail-recursive, but still allocates memory
> off the heap during each iteration.  As the loop should only be run when
> the mouse button is pressed, the check_mouse succeeds when the button is
> pressed, but fails otherwise.  Commenting out the check_mouse or do_eval
> call does not necessarily stop the heap allocation, although commenting
> out both certainly does (of course).  The easiest solution would be to
> make a predicate fail somewhere, but, then, how would I be able to get a
> return value?  (Using reference vars or internal dbases seems to make the
> program run unacceptably slow).
> 
> Tim G.

If you want both input to and output from a predicate, it must have a arity
greater than one:

                loop(In,Out):-
                      check_mouse,
                      process(In,Out).

It's not clear from your example why you need a recursive structure.  However,
Prolog will eat lots of memory if a non-deterministic predicate is called
within the clause.  To avoid this, ensure that called predicates are
non-deterministic, or use a cut after the non-deterministic call:

                loop(In,In):-
                      condition(satisfied).

                loop(In,Out):-
                      process(In,In2),!,    %"process" is non-deterministic
                      ....
                      loop(In2,Out).

Note that variable "Out" remains unbound until the desired condition is
satisfied.

'Hope this helps,

Euan Mason
University of Canterbury, NZ.

jgarland@kean.ucs.mun.ca (08/14/90)

In article <kalQPim00WB242VkMS@andrew.cmu.edu>, tg1e+@andrew.cmu.edu (Timothy R. Gottschalk) writes:
> 
Re the following Turbo (PDC) Prolog structure:
> 
>   loop(In):-
>       check_mouse,
>       do_eval(In, Out),
>       loop(Out).
>   loop(_):-
>       do_exit.
> 
> The compiler recognizes that it is tail-recursive, but still allocates memory
                                     ^^^^^^^^^^^^^^????? mine does'nt--it   
                                          says loop is nondeterminstic
> off the heap during each iteration.  As the loop should only be run when
          ^^^^???  Did you check this--mine 
              allocates *stack* space which is a clue to what's happening


> The easiest solution would be to
> make a predicate fail somewhere, but, then, how would I be able to get a
> return value?  
See below.

> 
> Tim G.


The structure you're trying to use is common in Turbo (now PDC) Prolog--though
it does violate many principles of good Prolog programming practice in other 
implementations. 

Below you'll find 2 listings which get at your trouble.  Trace through 
them.  Note that the main problem is that your predicate is NOT able to take
advantage of tail recursion optimization and so *stack*, not heap, space 
is used on each iteration as you trace through the predicate loop_1/1 
but not when you trace through loop_1_withcut/1

As these listings are in PDC Prolog (next version up from TP2) you'll need to
change the storage predicate back to the Turbo way of doing things and add a
few write statements. 

A last note is that the PDC Prolog Toolbox includes a number of mouse-handling 
predicates that are quite fast.  I also seem to remember these being 
published in the now-defunct Turbo Technix magazine in 1988.



/********* Listing 1 ************************************/

shorttrace      % Don't use 'trace' as it turns tail recursion off
diagnostics     % NOTE loop_1 will be NONdeterministic and will generate
                % backtracking points on each iteration and consume 
                % stack space

predicates

  loop_1(integer)
  
clauses

  loop_1(X) :-
  
    storage,      % PDC PREDICATE--REPLACE WITH TP2 PREDICATE AND WRITES
    X < 10,       % equivalent to check_mouse
    Out=X+1,      % equivalent to do_eval
    loop_1(Out).  % recursive call, push this iteration on to stack as we may
                  % want to return to backtrack points
    
  loop_1(_).      % succeed gracefully
    
  
  
goal

  loop_1(1).      % NOTE that the *stack* space is decremented each iteration

/*********** Listing 2 ***********************************/

  
shorttrace       % Don't use 'trace' as it turns tail recursion off
diagnostics      % NOTE loop_1_withcut will be deterministic and will not
                 % generate backtracking points on each iteration and 
                 % so will not consume stack space (with t-r 
                 % optimization on)
  
predicates  
  
  loop_1_withcut(integer)

clauses

  loop_1_withcut(X) :-
  
  
    storage,             % PDC PREDICATE--REPLACE WITH TP2 PREDICATE AND WRITES
    X < 10,              % equivalent to check_mouse

    !,                   % NULLIFY BACKTRACKING POSSIBILITY once we pass check
                         % NOTE: put as early in the clause as you logically can

    Out=X+1,             % equivalent to do_eval
    loop_1_withcut(Out). % tail recursive call, do not push this iteration 
                         % on to stack
  
  loop_1_withcut(_).     % succeed gracefully

goal

  loop_1_withcut(1). % NOTE that *no* space is lost each iteration
  
*************************** end of listings *********************