More on Boolean Shortcircuiting



Dear Forthers,

I have been giving boolean short-circuiting more thought, and would
appreciate your comments.

Consider the example of having some image handles on the data stack. The
task is to assign these to one of two variables ( FIRST and SECOND ),
given some criteria. FIRST must be tried first, then SECOND. Rejected
image handles are dropped. The number of images is given in COUNT.

A first pass solution:

variable first
variable second

\ Aborts the processing if there are too few images.
: too-few-images? ( -- )
count @ 2 < "Too few images." log-and-exit! ;

\ Criteria for image handles
: nil? ( tgt -- f )
: newer? ( src tgt -- f ) ... ;
: higher-priority? ( src tgt -- f ) ... ;

\ Runs the criteria checks for an image against a variable.
: replace? ( src tgt -- src tgt f )
dup nil? if true exit then
2dup newer? if true exit then
2dup higher-priority? if true exit then
false ;

\ Assigns image handles on the data stack.
: process ( image* -- )
too-few-images?
count @ 0 do
first @ replace?
if first !
else second @ replace?
if second !
else drop then
then
loop;

This does the job, but:

1) REPLACE? could be tidied up with boolean short-circuiting,
2) The nesting of PROCESS can quickly become a mess if later more
variables are added later (THIRD, FOURTH, etc).
3) The code is not very clear, even for this simple example.
4) Using an exception to abort processing means it must be caught
somewhere by PROCESS's caller.

One obvious factoring is to use a short-circuiting operator:

: | ` if ` true ` exit ` then ; immediate

With which REPLACE? becomes:

: replace? ( src tgt -- src tgt f )
dup nil? | 2dup newer? | 2dup higher-priority? | false ;

Which is more readable. We can also then dispense with using exceptions
in TOO-FEW-IMAGES?

: too-few-images? ( -- f )
count @ 2 < dup if "Too few images to process." log then ;

: process ( image* -- f )
too-few-images? | count @ 0 do ... false ;

And the nested IFs in PROCESS can be factored too:

: assign? ( img xt -- img F | T )
tuck @ replace? nip if swap ! true else swap drop false then ;

: assign ( img -- f )
first assign? | second assign? | drop false ;

: assign-all ( images -- f )
count @ 0 do assign drop loop false ;

\ False indicates success.
: process ( images* -- f )
too-few-images? | assign-all ;

Which is more readable. But ASSIGN? is a mess and the return value of
PROCESS is not intuitive. Both could be improved using another short-
circuiting operator:

: -> ` not ` if ` false ` exit ` then ; immediate

: assign? ( img xt -- img f )
@ replace? nip ;

: assign-first? ( src -- src F | T )
first assign? -> first ! true ;

: assign-second? ( src -- src F | T )
second assign? -> second ! true ;

: assign ( src -- f )
assign-first? | assign-second? | drop false ;

: assign-all ( images -- f )
count @ 0 do assign drop loop true ;

\ Returns true on success.
: process ( images* -- f )
too-few-images? not -> assign-all ;

This code looks clearer to me, and extensions to include more variables/
criteria is obvious. Also, there is no need to catch exceptions.

Do you think the short-circuiting operators:

: | ` if ` true ` exit ` then ; immediate
: -> ` not ` if ` false ` exit ` then ; immediate

have general utility outside this simple example? Can they be improved?

Thanks,
Arnold
.



Relevant Pages

  • Re: More on Boolean Shortcircuiting
    ... I have been giving boolean short-circuiting more thought, ... if true exit then ... ELSES true THEN; ...
    (comp.lang.forth)
  • Re: Boolean Shortcircuiting
    ... Arnold Doray wrote: ... Is there a way to implement boolean short-circuiting in Forth? ... if true exit then ... dup corrupted? ...
    (comp.lang.forth)
  • Re: Boolean Shortcircuiting
    ... Arnold Doray wrote: ... Is there a way to implement boolean short-circuiting in Forth? ... if true exit then ...
    (comp.lang.forth)