Requirement - A Sudoku solver, using logic (emulating human techniques) to solve a puzzle. There are (in this example) five different kinds of transformations that can performed upon the data set. We want to keep repeating the first transformation until there is no change; upon no change, we want to proceed to the second transformation. If the second transformation results in a change, we want to go back and start with the first transformation; otherwise we proceed onto the third transformation, and so on.
Do Until ( ¬ changed ) changed = test001() If ( ¬ changed ) changed = test002() If ( ¬ changed ) changed = test003() If ( ¬ changed ) changed = test004() If ( ¬ changed ) changed = test005() EndIf EndIf EndIf EndIf EndDo
That's a lot of
EndIfs, and what would happen if we had two dozen
transformations, or a thousand?
This whole top down structure can be implemented bottom up, through the use of Table Driven logic.
First, we create an array of Procedure Pointers:
d ds d * ProcPtr Inz(%PAddr(test001)) d * ProcPtr Inz(%PAddr(test002)) d * ProcPtr Inz(%PAddr(test003)) d * ProcPtr Inz(%PAddr(test004)) d * ProcPtr Inz(%PAddr(test005)) d * ProcPtr Inz(*Null) d @test@ 1 96* ProcPtr Dim(6)
Note that there is one more entry in the array than there are transformations to execute.
Now, we define a pointer based procedure:
d test pr 1n ExtProc(test@)
and its basing pointer:
d test@ s * ProcPtr
and initialize tstCnt:
d tstCnt s 3s 0 Inz(%Elem(@test@))
Now, we can implement the previous top down structure thusly;
DoU ( $I = tstCnt ); $I = 0; DoU ( $I = tstCnt Or test() ); $I = $I + 1; test@ = @test@($I); EndDo; EndDo;
If we process all five transformation without change, then on the last
$I goes from five to six (5=>6),
test@ is loaded with a
*NULL, and we fall out of our loop.
I have, in production code, implemented a Sarbanes-Oxley compliance report with a similar table driven structure. The process involves (roughly) fifteen different tests, and we want to fall out upon the first failure. The actual production mainline looks like:
$I = 0; DoU ( msgID <> *Blanks Or $I = s100Cnt ); $I = $I + 1; procProxy@ = @procProxy@(@s100($I)); procProxy(); EndDo; cmtHndlr();
msgID is a global variable containing any error messages.
s100Cnt is the number of elements in
@s100 contains indexes to procedure pointers in
this allows us to change logic at run-time (see note) - if hard coding data is a bad thing,
then it seems to reason hard coding logic is as well. This technique allow us to
soft code logic.
@s100 is actually pointer based, allowing us to determine which of
multiple arrays to use:
d @s100 s 3s 0 Based(@s100@) Dim(15) Select; When ( system = SYSW And Not pmTESTMODE ); @s100@ = %Addr(@S100W); s100Cnt = %Elem(@S100W); When ( system = SYSW And pmTESTMODE ); @s100@ = %Addr(@S100WT); s100Cnt = %Elem(@S100WT); When ( system = SYSO And Not pmTESTMODE ); @s100@ = %Addr(@S100O); s100Cnt = %Elem(@S100O); When ( system = SYSO And pmTESTMODE ); @s100@ = %Addr(@S100OT); s100Cnt = %Elem(@S100OT); EndSl;