Ben Langhinrichs

Photograph of Ben Langhinrichs

E-mail address - Ben Langhinrichs






November, 2004
SMTWTFS
 01 02 03 04 05 06
07 08 09 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

Search the weblog





























Genii Weblog


Civility in critiquing the ideas of others is no vice. Rudeness in defending your own ideas is no virtue.


Thu 11 Nov 2004, 03:19 PM
In working with @Midas Formulas, I am reminded as I have not been for a while how convoluted complex formulas can look.  The formatting is not simple, and the formulas tend to look like badly run on sentences.  In any case, here are three tips on how to make a complex formula a bit more readable.

I will start with a gnarly example from my unreleased Hide-When 1.0 sample database showing how the Midas Rich Text LSX and @Midas Formulas can work with hide-when formulas.  I have a nasty sort of agent which prompts for one of a selected set of criteria and then loops through setting hide-when flags on any paragraph which matches.

Original formula code
choice := @Prompt([OkCancelList]; "Select criteria"; "Select which paragraphs to hide from read mode:"; "1) Those which do not start with D"; "1) Those which do not start with D":"2) Those with images":"3) Those with the substring 'Raven' in them");
p := 1;
t := @DbCommand("Midas":"NoCache"; "GetCount"; @DocumentUniqueID; "Body"; "Paragraph");
@For(p:=1; p <= t; p := p+1;
   @If(@Begins(choice; "1"); 
              @If(!@Begins(@DbCommand("Midas":"NoCache"; "Text"; @DocumentUniqueID; "Body":("Paragraph "+@Text(p))); "D");
               @DbCommand("Midas":"NoCache"; "ParagraphStyle"; @DocumentUniqueID; "Body":("Paragraph "+@Text(p)); "+HIDE_READ +HIDE_PREVIEW_Read"); "");
          @Begins(choice; "2"); 
              @If(@DbCommand("Midas":"NoCache"; "GetCount"; @DocumentUniqueID; "Body":("Paragraph "+@Text(p)); "Graphic") > 0;
               @DbCommand("Midas":"NoCache"; "ParagraphStyle"; @DocumentUniqueID; "Body":("Paragraph "+@Text(p)); "+HIDE_READ +HIDE_PREVIEW_Read"); "");
          @Begins(choice; "3"); 
              @If(@Contains(@DbCommand("Midas":"NoCache"; "UnformattedText"; @DocumentUniqueID; "Body":("Paragraph "+@Text(p))); "Raven");
               @DbCommand("Midas":"NoCache"; "ParagraphStyle"; @DocumentUniqueID; "Body":("Paragraph "+@Text(p)); "+HIDE_READ +HIDE_PREVIEW_Read"); "");
""));
SELECT @All

Tip 1: Use local variables to replace lists, strings or other "constants"

These five steps are all variations on this tip, taken to simplify and shorten the formula.

1) Make the list of choices into variable 'c', which allows us to use c[1] in the @Prompt.
2) Turn the repeated list "Midas":"NoCache" into a one letter variable 'm'.
3) Inside the iterative loop, replace the repeated computed list "Body":("Paragraph "+@Text(p)); into variable 'bp'
4) Turn the repeated string "+HIDE_READ +HIDE_PREVIEW_Read" into a two letter variable 'hf' (for hide formula).
5) Turn the repeated @DocumentUniqueID into a single character variable 'u'.

c := "1) Those which do not start with D":"2) Those with images":"3) Those with the substring 'Raven' in them";
m := "Midas":"NoCache";
u := @DocumentUniqueID;
hf := "+HIDE_READ +HIDE_PREVIEW_Read";
choice := @Prompt([OkCancelList]; "Select criteria"; "Select which paragraphs to hide from read mode:"; c[1]; c);
p := 1;
t := @DbCommand(m; "GetCount"; u; "Body"; "Paragraph");
@For(p:=1; p <= t; p := p+1;
   bp := "Body":("Paragraph "+@Text(p));
   @If(@Begins(choice; "1");
              @If(!@Begins(@DbCommand(m; "Text"; u; bp); "D");
                      @DbCommand(m; "ParagraphStyle"; u; bp; hf); "");
          @Begins(choice; "2");
              @If(@DbCommand(m; "GetCount"; u; bp; "Graphic") > 0;
                      @DbCommand(m; "ParagraphStyle"; u; bp; hf); "");
          @Begins(choice; "3");
              @If(@Contains(@DbCommand(m; "UnformattedText"; u; bp); "Raven");
                      @DbCommand(m; "ParagraphStyle"; u; bp; hf); "");
""));
SELECT @All

Tip 2: If the same action happens as a result of separate tests, collapse nested ifs and save boolean

1) Since the @DbCommand(m; "ParagraphStyle"; ""; u; bp; hf); is repeated, separate it out and save condition in boolean variable 'do_it'

c := "1) Those which do not start with D":"2) Those with images":"3) Those with the substring 'Raven' in them";
m := "Midas":"NoCache";
hf := "+HIDE_READ +HIDE_PREVIEW_Read";
u := @DocumentUniqueID;
choice := @Prompt([OkCancelList]; "Select criteria"; "Select which paragraphs to hide from read mode:"; c[1]; c);
p := 1;
t := @DbCommand(m; "GetCount"; u; "Body"; "Paragraph");
@For(p:=1; p <= t; p := p+1;
   bp := "Body":("Paragraph "+@Text(p));
   do_it := @If(@Begins(choice; "1") & !@Begins(@DbCommand(m; "Text"; u; bp); "D");
                              @True;
                          @Begins(choice; "2") & @DbCommand(m; "GetCount"; u; bp; "Graphic") > 0;
                              @True;
                       @Begins(choice; "3") & @Contains(@DbCommand(m; "UnformattedText"; u; bp); "Raven");
                              @True;
                              @False);
   @If (do_it; @DbCommand(m; "ParagraphStyle"; u; bp; hf); "");
);
SELECT @All

Tip 3: Combine separate tests with ands and ors

This tip does not always make sense, since there are no short circuited expressions as there are in C/C++ and Java.  This means that all tests will be performed, even ones with side effects, so be careful.
1) Combine the separate choice tests with parantheses and 'or' vertical bars.

c := "1) Those which do not start with D":"2) Those with images":"3) Those with the substring 'Raven' in them";
m := "Midas":"NoCache";
hf := "+HIDE_READ +HIDE_PREVIEW_Read";
u := @DocumentUniqueID;
choice := @Prompt([OkCancelList]; "Select criteria"; "Select which paragraphs to hide from read mode:"; c[1]; c);
p := 1;
t := @DbCommand(m; "GetCount"; u; "Body"; "Paragraph");
@For(p:=1; p <= t; p := p+1;
   bp := "Body":("Paragraph "+@Text(p));
   @If((@Begins(choice; "1") & !@Begins(@DbCommand(m; "Text"; u; bp); "D")) |
           (@Begins(choice; "2") & @DbCommand(m; "GetCount"; u; bp; "Graphic") > 0) |
           (@Begins(choice; "3") & @Contains(@DbCommand(m; "UnformattedText"; u; bp); "Raven"));
                @DbCommand(m; "ParagraphStyle"; u; bp; hf);
                ""
          )
);
SELECT @All

Well, there you have it.  It still isn't pretty, but it is a whole lot cleaner than the original.  Is there more I could have done?  I'd be happy to hear any feedback.

Copyright © 2004 Genii Software Ltd.