-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathchapter8.tex
More file actions
1898 lines (1593 loc) · 69.4 KB
/
chapter8.tex
File metadata and controls
1898 lines (1593 loc) · 69.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
%% Thinking Forth
%% Copyright (C) 2004 Leo Brodie
%% Initial transcription by Nils M Holm
%% Based on OCR scans by Steve Fisher
%%
%% Chapter: Eight - Minimizing Control Structures
%!! horizontal line
%EIGHT
%!! horizontal line
\chapter{Minimizing Control~Structures}\Chapmark{8}%
\index{C!Control structure minimization|(}%
\initial Control structures aren't as important in \Forth{} as they
are in other languages. \Forth{} programmers tend to write very
complex applications in terms of short words, without much emphasis on
\forth{IF THEN} constructs.
There are several techniques for minimizing control structures.
They include:
%!! items should be preceded by bullets
\begin{itemize}
\item computing or calculating
\item hiding conditionals through re-factoring
\item using structured exits
\item vectoring
\item redesigning.
\end{itemize}
In this chapter we'll examine these techniques for simplifying and
eliminating control structures from your code.
\section{What's So Bad about Control Structures?}%
\index{C!Control structure minimization!reasons for|(}
Before we begin reeling off our list of tips, let's pause to examine why
conditionals should be avoided in the first place.
The use of conditional structures adds complexity to your code. The
more complex your code is, the harder it will be for you to read and to
maintain. The more parts a machine has, the greater are its chances of
breaking down. And the harder it is for someone to fix.
%!! horizontal line
\begin{interview}
\person{Moore}\index{M!Moore, Charles|(} tells this story:
\begin{tfquot}
I recently went back to a company we had done some work for several years
ago. They called me in because their program is now five years old, and
it's gotten very complicated. They've had programmers going in and
patching things, adding state variables and conditionals. Every statement
that I recall being a simple thing five years ago, now has gotten very
complicated. ``If this, else if this, else if this'' \dots{} and then the
simple thing.
Reading that statement now, it's impossible for me to figure out what it's
doing and why. I'd have to remember what each variable indicated, why it
was relevant in this case, and then what was happening as a consequence of
it---or not happening.
It started innocently. They had a special case they needed to worry about.
To handle that special case, they put a conditional in one place. Then they
discovered that they also needed one here, and here. And then a few more.
Each incremental step only added a little confusion to the program. Since
they were the programmers, they were right on top of it.
The net result was disastrous. In the end they had half a dozen flags.
Test this one, reset it, set that one, and so on. As a result of this
condition, you knew you had other conditions coming up you had to look out
for. They created the logical equivalent of spaghetti code in spite of the
opportunity for a structured program.
The complexity went far beyond what they had ever intended. But they'd
committed themselves to going down this path, and they missed the simple
solution that would have made it all unnecessary---having two words
instead of one. You either say \forth{GO} or you say \forth{PRETEND}.
In most applications there are remarkably few times when you need to test
the condition. For instance in a video game, you don't really say ``If he
presses Button A, then do this; if he presses Button B, then do something
else.'' You don't go through that kind of logic.
If he presses the button, you do something. What you do is associated with
the button, not with the logic.
Conditionals aren't bad in themselves---they are an essential construct. But
a program with a lot of conditionals is clumsy and unreadable. All you can
do is question each one. Every conditional should cause you to ask, ``What
am I doing wrong?''
What you're trying to do with the conditional can be done in a different
way. The long-term consequences of the different way are preferable to the
long-term consequences of the conditional.
\end{tfquot}\index{M!Moore, Charles|)}
\end{interview}%
\index{C!Control structure minimization!reasons for|)}%
Before we introduce some detailed techniques, let's look at three
approaches to the use of conditionals in a particular example.
\Fig{fig8-1}, \Fig{fig8-2}, and \Fig{fig8-3} illustrate three versions of
a design for an automatic teller machine.\program{teller1}
\begin{figure*}[tttt]
\verbfig{fig8-1}{The structured approach.}
\begin{center}
\small\begin{BVerbatim}[commandchars=\&\{\},baselinestretch=0.85]
&underline{AUTOMATIC-TELLER}
IF card is valid DO
IF card owner is valid DO
IF request withdrawal DO
IF authorization code is valid DO
query for amount
IF request &(&le&) current balance DO
IF withdrawal &(&le&) available cash DO
vend currency
debit account
ELSE
message
terminate session
ELSE
message
terminate session
ELSE
message
terminate session
ELSE
IF authorization code is valid DO
query for amount
accept envelope through hatch
credit account
ELSE
message
terminate session
ELSE
eat card
ELSE
message
END
\end{BVerbatim}
\end{center}
\end{figure*}
The first example comes straight out of the School for Structured
Programmers. The logic of the application depends on the correct nesting
of \forth{IF} statements.
Easy to read? Tell me under what condition the user's card gets eaten. To
answer, you have to either count \forth{ELSE}s from the bottom and match
them with the same number of \forth{IF}s from the top, or use a
straightedge.
\begin{figure*}[tttt]
\verbfig{fig8-2}{Nesting conditionals within named procedures.}
\small\begin{center}
\begin{BVerbatim}[commandchars=\&\{\},baselinestretch=0.85]
&underline{AUTOMATIC-TELLER}
PROCEDURE READ-CARD
IF card is readable THEN CHECK-OWNER
ELSE eject card END
PROCEDURE CHECK-OWNER
IF owner is valid THEN CHECK-CODE
ELSE eat card END
PROCEDURE CHECK-CODE
IF code entered matches owner THEN TRANSACT
ELSE message, terminate session END
PROCEDURE TRANSACT
IF requests withdrawal THEN WITHDRAW
ELSE DEPOSIT END
PROCEDURE WITHDRAW
Query
If request &(&le&) current balance THEN DISBURSE END
PROCEDURE DISBURSE
IF disbursement &(&le&) available cash THEN
vend currency
debit account
ELSE message END
PROCEDURE DEPOSIT
accept envelope
credit account
\end{BVerbatim}
\end{center}
\end{figure*}
The second version, \Fig{fig8-2}, shows the improvement that using many
small, named procedures can have on readability. The user's card is eaten
if the owner is not valid.
But even with this improvement, the design of each word depends completely
on the \emph{sequence} in which the tests must be performed. The
supposedly ``highest'' level procedure is burdened with eliminating the
worst-case, most trivial kind of event. And each test becomes responsible
for invoking the next test.
\begin{figure*}[tttt]
\verbfig{fig8-3}{Refactoring and/or eliminating conditionals.}
\begin{center}
\small\begin{BVerbatim}[commandchars=\&\{\},baselinestretch=0.85]
&underline{AUTOMATIC-TELLER}
: RUN
READ-CARD CHECK-OWNER CHECK-CODE TRANSACT ;
: READ-CARD
valid code sequence NOT readable IF eject card QUIT
THEN ;
: CHECK-OWNER
owner is NOT valid IF eat card QUIT THEN ;
: CHECK-CODE
code entered MISmatches owner's code IF message QUIT
THEN ;
: READ-BUTTON ( -- adr-of-button's-function)
( device-dependent primitive) ;
: TRANSACT
READ-BUTTON EXECUTE ;
1 BUTTON WITHDRAW
2 BUTTON DEPOSIT
: WITHDRAW
Query
request &(&le&) current balance IF DISBURSE THEN ;
: DISBURSE
disbursement &(&le&) available cash IF
vend currency
debit account
ELSE message THEN ;
: DEPOSIT
accept envelope
credit account ;
\end{BVerbatim}
\end{center}
\end{figure*}
The third version comes closest to the promise of \Forth{}. The highest
level word expresses exactly what's happening conceptually, showing only
the main path. Each of the subordinate words has its own error exit, not
cluttering the reading of the main word. One test does not have to invoke
the next test.
Also \code{TRANSACT} is designed around the fact that the user will make
requests by pressing buttons on a keypad. No conditions are necessary. One
button will initiate a withdrawal, another a deposit. This approach
readily accommodates design changes later, such as the addition of a
feature to transfer funds. (And this approach does not thereby become
dependent on hardware. Details of the interface to the keypad may be
hidden within the keypad lexicon, \code{READ-BUTTON} and \code{BUTTON}.)
Of course, \Forth{} will allow you to take any of the three approaches.
Which do you prefer?\program{teller2}
\section{How to Eliminate Control Structures}
In this section we'll study numerous techniques for simplifying or
avoiding conditionals. Most of them will produce code that is more
readable, more maintainable, and more efficient. Some of the techniques
produce code that is more efficient, but not always as readable.
Remember, therefore: Not all of the tips will be applicable in all
situations.
\subsection{Using the Dictionary}%
\index{C!Control structure minimization!dictionary|(}%
\index{D!Dictionary:!control structure minimization with|(}
\begin{tip}
Give each function its own definition.
\end{tip}
By using the \Forth{} dictionary properly, we're not actually eliminating
conditionals; we're merely factoring them out from our application code.
The \Forth{} dictionary is a giant string case statement. The match and
execute functions are hidden within the \Forth{} system.
\begin{interview}
\person{Moore}:\index{M!Moore, Charles|(}
\begin{tfquot}
In my accounting package, if you receive a check from somebody, you type
the amount, the check number, the word \forth{FROM}, and the person's
name:
\begin{Code}
200.00 127 FROM ALLIED
\end{Code}
The word \forth{FROM} takes care of that situation. If you want to bill
someone, you type the amount, the invoice number, the word \forth{BILL}
and the person's name:
\begin{Code}
1000.00 280 BILL TECHNITECH
\end{Code}
\end{tfquot}\index{M!Moore, Charles|)}
\dots{} One word for each situation. The dictionary is making the decision.\medskip
\end{interview}
This notion pervades \Forth{} itself. To add a pair of single-length
numbers we use the command \forth{+}. To add a pair of double-length
numbers we use the command \forth{D+}. A less efficient, more complex
approach would be a single command that somehow ``knows'' which type of
numbers are being added.
\Forth{} is efficient because all these words---\forth{FROM} and
\forth{BILL} and \forth{+} and \forth{D+}---can be implemented without any
need for testing and branching.
\index{D!Dumb words|(}
\begin{tip}
Use dumb words.
\end{tip}
This isn't advice for TV writers. It's another instance of using the
dictionary. A ``dumb'' word is one that is not state-dependent, but
instead, has the same behavior all the time (``referentially
transparent'').
A dumb word is unambiguous, and therefore, more trustworthy.
A few common \Forth{} words have been the source of controversy recently
over this issue. One such word is \forth{."} which prints a string. In
its simplest form, it's allowed only inside a colon definition:
\begin{Code}
: TEST ." THIS IS A STRING " ;
\end{Code}
Actually, this version of the word does \emph{not} print a string. It
\emph{compiles} a string, along with the address of another definition
that does the printing at run time.
This is the dumb version of the word. If you use it outside a colon
definition, it will uselessly compile the string, not at all what a
beginner might expect.
To solve this problem, the FIG model added a test inside \forth{."} that
determined whether the system was currently compiling or interpreting. In
the first case, \forth{."} would compile the string and the address of the
primitives; in the second case it would \forth{TYPE} it.
\forth{."} became two completely different words housed together in one
definition with an \forth{IF ELSE THEN} structure. The flag that indicates
whether \Forth{} is compiling or interpreting is called \forth{STATE}.
Since the \forth{."} depends on \forth{STATE}, it is said to be
``\forth{STATE}-dependent,'' literally.
The command \emph{appeared} to behave the same inside and outside a colon
definition. This duplicity proved useful in afternoon introductions to
\Forth{}, but the serious student soon learned there's more to it than
that.
Suppose a student wants to write a new word called \forthb{B."} (for
``bright-dot-quote'') to display a string in bright characters on her
display, to be used like this:
\begin{Code}
." INSERT DISK IN " B." LEFT " ." DRIVE "
\end{Code}
She might expect to define \forth{B."} as
\begin{Code}
: B." BRIGHT ." NORMAL ;
\end{Code}
that is, change the video mode to bright, print the string, then reset the
mode to normal.
She tries it. Immediately the illusion is destroyed; the deception is
revealed; the definition won't work.
To solve her problem, the programmer will have to study the definition of
\forth{(.")} in her own system. I'm not going to get sidetracked here with
explaining how \forth{(.")} works---my point is that smartness isn't all
it appears to be.
Incidentally, there's a different syntactical approach to our student's
problem, one that does not require having two separate words, \forth{."}
and \forth{B."} to print strings. Change the system's \forth{(.")} so that
it always sets the mode to normal after typing, even though it will
already be normal most of the time. With this syntax, the programmer need
merely precede the emphasized string with the simple word \forth{BRIGHT}.
\begin{Code}
." INSERT DISK IN " BRIGHT ." LEFT " ." DRIVE "
\end{Code}
The '83 Standard now specifies a dumb \forth{."} and, for those cases
where an interpretive version is wanted, the new word \forth{.(} has been
added. Happily, in this new standard we're using the dictionary to make a
decision by having two separate words.
\index{S!STATE|(}
The word \forth{'} (tick) has a similar history. It was
\forthb{STATE}-dependent in fig-\Forth{}, and is now dumb in the '83
Standard. Tick shares with dot-quote the characteristic that a programmer
might want to reuse either of these words in a higher-level definition and
have them behave in the same way they do normally.%
\index{D!Dumb words|)}
\begin{tip}
Words should not depend on \forthb{STATE} if a programmer might ever want
to invoke them from within a higher-level definition and expect them to
behave as they do interpretively.
\end{tip}
\forthb{ASCII}\index{A!ASCII} works well as a \forth{STATE}-dependent
word, and so does \forthb{MAKE}.\index{M!MAKE} (See \App{C}.)%
\index{S!STATE|)}%
\index{C!Control structure minimization!dictionary|)}%
\index{D!Dictionary:!control structure minimization with|)}
\subsection{Nesting and Combining Conditionals}%
\index{N!Nesting conditionals|(}%
\index{C!Conditionals, nesting and combining|(}%
\index{C!Control structure minimization!nesting and combining conditionals|(}
\begin{tip}
Don't test for something that has already been excluded.
\end{tip}
Take this example, please:
\begin{Code}
: PROCESS-KEY
key dup LEFT-ARROW = IF CURSOR-LEFT THEN
dup RIGHT-ARROW = IF CURSOR-RIGHT THEN
dup UP-ARROW = IF CURSOR-UP THEN
DOWN-ARROW = IF CURSOR-DOWN THEN ;
\end{Code}
This version is inefficient because all four tests must be made regardless
of the outcome of any of them. If the key pressed was the left-arrow key,
there's no need to check if it was some other key.
Instead, you can nest the conditionals, like this:
\begin{Code}
: PROCESS-KEY
key dup LEFT-ARROW = IF CURSOR-LEFT ELSE
dup RIGHT-ARROW = IF CURSOR-RIGHT ELSE
dup UP-ARROW = IF CURSOR-UP ELSE
CURSOR-DOWN
THEN THEN THEN drop ;
\end{Code}
\begin{tip}
Combine booleans of similar weight.
\end{tip}
Many instances of doubly-nested \forthb{IF }\forthb{THEN} structures can
be simplified by combining the flags with logical operators before making
the decision.
Here's a doubly-nested test:
\begin{Code}
: ?PLAY SATURDAY? IF WORK FINISHED? IF
GO PARTY THEN THEN ;
\end{Code}
The above code uses nested \forthb{IF}s to make sure that it's both
Saturday and the chores are done before it boogies on down. Instead,
let's combine the conditions logically and make a single decision:
\begin{Code}
: ?PLAY SATURDAY? WORK FINISHED? and IF
GO PARTY THEN ;
\end{Code}
It's simpler and more readable.
The logical ``or'' situation, when implemented with
\forthb{IF }\forthb{THEN}s, is even clumsier:
\begin{Code}
: ?RISE PHONE RINGS? IF UP GET THEN
ALARM-CLOCK RINGS? IF UP GET THEN ;
\end{Code}
This is much more elegantly written as
\begin{Code}
: ?RISE PHONE RINGS? ALARM RINGS? or IF UP GET THEN ;
\end{Code}
One exception to this rule arises when the speed penalty for checking
some of the conditions is too great.
We might write
\begin{Code}
: ?CHOW-MEIN BEAN-SPROUTS? CHOW-MEIN RECIPE? and IF
CHOW-MEIN PREPARE THEN ;
\end{Code}
But suppose it's going to take us a long time to hunt through our recipe
file to see if there's anything on chow mein. Obviously there's no point in
undertaking the search if we have no bean sprouts in the fridge. It would
be more efficient to write
\begin{Code}
: ?CHOW-MEIN BEAN-SPROUTS? IF CHOW-MEIN RECIPE? IF
CHOW-MEIN PREPARE THEN THEN ;
\end{Code}
We don't bother looking for the recipe if there are no sprouts.
Another exception arises if any term is probably not true. By
eliminating such a condition first, you avoid having to try the other
conditions.
\begin{tip}
When multiple conditions have dissimilar weights (in likelihood or
calculation time) nest conditionals with the term that is least likely
to be true or easiest to calculate on the outside.
\end{tip}
Trying to improve performance in this way is more difficult with the
\forth{OR} construct. For instance, in the definition
\begin{Code}
: ?RISE PHONE RINGS? ALARM RINGS? or IF UP GET THEN ;
\end{Code}
we're testing for the phone and the alarm, even though only one of them
needs to ring for us to get up. Now suppose it were much more difficult to
determine that the alarm clock was ringing. We could write
\begin{Code}
: ?RISE PHONE RINGS? IF UP GET ELSE
ALARM-CLOCK RINGS? IF UP GET THEN THEN ;
\end{Code}
If the first condition is true, we don't waste time evaluating the second.
We have to get up to answer the phone anyway.
The repetition of \forth{UP GET} is ugly---not nearly as readable as the
solution using \forth{OR}---but in some cases desirable.%
\index{C!Conditionals, nesting and combining|)}%
\index{C!Control structure minimization!nesting and combining conditionals|)}
\index{N!Nesting conditionals|)}%
\subsection{Choosing Control Structures}%
\index{C!Control structures:!choosing|(}
\begin{tip}
The most elegant code is that which most closely matches the problem.
Choose the control structure that most closely matches the control-flow
problem.
\end{tip}%
\index{C!Control structures:!choosing|)}
\subsubsection{Case Statements}%
\index{C!Case statements|(}%
\index{C!Control structure minimization!case statements|(}
A particular class of problem involves selecting one of several possible
paths of execution according to a numeric argument. For instance, we
want the word \forth{.SUIT} to take a number representing a suit of playing
cards, 0 through 3, and display the name of the suit. We might define this
word using nested \forthb{IF }\forthb{ELSE }\forthb{THEN}s, like this:
\begin{Code}
: .SUIT ( suit -- )
dup 0= IF ." HEARTS " ELSE
dup 1 = IF ." SPADES " ELSE
dup 2 = IF ." DIAMONDS " ELSE
." CLUBS "
THEN THEN THEN drop ;
\end{Code}
We can solve this problem more elegantly by using a ``case statement.''
Here's the same definition, rewritten using the ``\person{Eaker} case
statement'' format, named after Dr.\@ \person{Charles E. Eaker}, the
gentleman who proposed it \cite{eaker}.
\begin{Code}
: .SUIT ( suit -- )
CASE
0 OF ." HEARTS " ENDOF
1 OF ." SPADES " ENDOF
2 OF ." DIAMONDS " ENDOF
3 OF ." CLUBS " ENDOF ENDCASE ;
\end{Code}
The case statement's value lies exclusively in its readability and
writeability. There's no efficiency improvement either in object memory or
in execution speed. In fact, the case statement compiles much the same
code as the nested \forthb{IF }\forthb{THEN} statements. A case statement
is a good example of compile-time factoring.
Should all \Forth{} systems include such a case statement? That's a matter
of controversy. The problem is twofold. First, the instances in which a
case statement is actually needed are rare---rare enough to question its
value. If there are only a few cases, a nested \forthb{IF }\forthb{ELSE
}\forthb{THEN} construct will work as well, though perhaps not as
readably. If there are many cases, a decision table is more flexible.
Second, many case-like problems are not quite appropriate for the case
structure. The \person{Eaker} case statement assumes that you're testing
for equality against a number on the stack. In the instance of
\forth{.SUIT}, we have contiguous integers from zero to three. It's more
efficient to use the integer to calculate an offset and directly jump to
the right code.
In the case of our Tiny Editor, later in this chapter, we have not one, but
two, dimensions of possibilities. The case statement doesn't match that
problem either.
Personally, I consider the case statement an elegant solution to a
misguided problem: attempting an algorithmic expression of what is
more aptly described in a decision table.
A case statement ought to be part of the application when useful,
but not part of the system.%
\index{C!Case statements|)}%
\index{C!Control structure minimization!case statements|)}
\subsubsection{Looping Structures}%
\index{C!Control structure minimization!looping structures|(}%
\index{L!Loops|(}
The right looping structure can eliminate extra conditionals.
\begin{interview}
\person{Moore}:\index{M!Moore, Charles|(}
\begin{tfquot}
Many times conditionals are used to get out of loops. That particular use
can be avoided by having loops with multiple exit points.
This is a live topic, because of the multiple \forthb{WHILE} construct
which is in poly\Forth{} but hasn't percolated up to \Forth{} '83. It's a
simple way of defining multiple \forthb{WHILE}s in the same
\forthb{REPEAT}.
Also \person{Dean Sanderson}\index{S!Sanderson, Dean} [of \Forth{}, Inc.]
has invented a new construct that introduces two exit points to a
\forthb{DO }\forthb{LOOP}.\index{D!DO LOOP} Given that construction you'll
have fewer tests. Very often I leave a truth value on the stack, and if
I'm leaving a loop early, I change the truth value to remind myself that I
left the loop early. Then later I'll have an \forthb{IF} to see whether I
left the loop early, and it's just clumsy.
Once you've made a decision, you shouldn't have to make it again. With the
proper looping constructs you won't need to remember where you came from,
so more conditionals will go away.
This is not completely popular because it's rather unstructured. Or perhaps
it is elaborately structured. The value is that you get simpler programs.
And it costs nothing.
\end{tfquot}\index{M!Moore, Charles|)}
\end{interview}
Indeed, this is a live topic. As of this writing it's too early to make
any specific proposals for new loop constructs. Check your system's
documentation to see what it offers in the way of exotic looping
structures. Or, depending on the needs of your application, consider
adding your own conditional constructs. It's not that hard in \Forth{}.
I'm not even sure whether this use of multiple exits doesn't violate the
doctrine of structured programming. In a
\forthb{BEGIN }\forthb{WHILE }\forthb{REPEAT}
loop with multiple \forthb{WHILE}s, all the exits bring you to a common
``continue'' point: the \forthb{REPEAT}. But with \person{Sanderson}'s
\index{S!Sanderson, Dean} construct, you
can exit the loop by jumping \emph{past} the end of the loop, continuing
at an \forthb{ELSE}. There are two possible ``continue'' points.
This is ``less structured,'' if we can be permitted to say that. And
yet the definition will always conclude at its semicolon and return to the
word that invoked it. In that sense it is well-structured; the module has
one entry point and one exit point.
When you want to execute special code only if you did \emph{not} leave the
loop prematurely, this approach seems the most natural structure to use.
(We'll see an example of this in a later section, ``Using Structured
Exits.'')
%
\index{C!Counts vs. terminators|(}
\index{T!Terminators vs. counts|(}
\begin{tip}
Favor counts over terminators.
\end{tip}
\Forth{} handles strings by saving the length of the string in the first
byte. This makes it easier to type, move, or do practically anything with
the string. With the address and count on the stack, the definition of
\forthb{TYPE} can be coded:
\begin{Code}
: type ( a #) over + swap DO i c@ emit LOOP ;
\end{Code}
(Although \forthb{TYPE} really ought to be written in machine code.)
This definition uses no overt conditional. \forthb{LOOP} actually hides the
conditional since each loop checks the index and returns to \forthb{DO} if it
has not yet reached the limit.
If a delimiter were used, let's say ASCII null (zero), the definition
would have to be written:
\begin{Code}
: type ( a) BEGIN dup c@ ?dup WHILE emit 1+
REPEAT drop ;
\end{Code}
An extra test is needed on each pass of the loop. (\forthb{WHILE} is a
conditional operator.)
Optimization note: The use of \forthb{?DUP} in this solution is expensive
in terms of time because it contains an extra decision itself. A faster
definition would be:
\begin{Code}
: type ( a) BEGIN dup c@ dup WHILE emit 1+
REPEAT 2drop ;
\end{Code}
The '83 Standard applied this principle to \forthb{INTERPRET}
\index{I!INTERPRET}
which now accepts a count rather than looking for a terminator.
The flip side of this coin is certain data structures in which it's
easiest to \emph{link} the structures together. Each record points to the
next (or previous) record. The last (or first) record in the chain can be
indicated with a zero in its link field.
If you have a link field, you have to fetch it anyway. You might as
well test for zero. You don't need to keep a counter of how many records
there are. If you decrement a counter to decide whether to terminate,
you're making more work for yourself. (This is the technique used to
implement \Forth{}'s dictionary as a linked list.)%
\index{C!Counts vs. terminators|)}%
\index{C!Control structure minimization!looping structures|)}%
\index{L!Loops|)}%
\index{T!Terminators vs. counts|)}%
\subsubsection{Calculating Results}%
\index{C!Control structure minimization!calculating results|(}%
\index{C!Calculations|(}%
\begin{tip}
Don't decide, calculate.
\end{tip}
Many times conditional control structures are applied mistakenly to
situations in which the difference in outcome results from a difference in
numbers. If numbers are involved, we can calculate them. (In Chapter
Four see the section called ``Calculations vs. Data Structures vs. Logic.'')
%
\index{B!Booleans, as hybrid values|(}
\begin{tip}
Use booleans as hybrid values.
\end{tip}
This is a fascinating corollary to the previous tip, ``Don't decide,
calculate.'' The idea is that booleans, which the computer represents as
numbers, can efficiently be used to effect numeric decisions. Here's one
example, found in many \Forth{} systems:
\begin{Code}
: s>d ( n -- d) \ sign extend s to d
dup O< IF -1 ELSE 0 THEN ;
\end{Code}
(The purpose of this definition is to convert a single-length number to
double-length. A double-length number is represented as two 16-bit
values on the stack, the high-order part on top. Converting a positive
integer to double-length merely means adding a zero onto the stack, to
represent its high-order part. But converting a negative integer to
double-length requires ``sign extension;'' that is, the high-order part
should be all ones.)
The above definition tests whether the single-length number is negative.
If so, it pushes a negative one onto the stack; otherwise a zero. But
notice that the outcome is merely arithmetic; there's no change in
process. We can take advantage of this fact by using the boolean itself:
\begin{Code}
: s>d ( n -- d) \ sign extend s to d
dup O< ;
\end{Code}
This version pushes a zero or negative one onto the stack without a
moment's (in)decision.
\medbreak
(In pre-1983 systems, the definition would be:
\begin{Code}
: s>d ( n -- d) \ sign extend s to d
dup O< negate ;
\end{Code}
See \App{C}.)%
\index{B!Booleans, as hybrid values|)}\medbreak
\index{H!Hybrid values|(}%
We can do even more with ``hybrid values'':
%
\index{A!AND|(}
\begin{tip}
To effect a decision with a numeric outcome, use \forthb{AND}.
\end{tip}
In the case of a decision that produces either zero or a non-zero ``$n$,''
the traditional phrase
\begin{Code}
( ? ) IF n ELSE 0 THEN
\end{Code}
is equivalent to the simpler statement
\begin{Code}
( ? ) n and
\end{Code}
Again, the secret is that ``true'' is represented by $-1$ (all ones) in
'83 \Forth{} systems. \forthb{AND}ing ``$n$'' with the flag will either
produce ``$n$'' (all bits intact) or ``$0$'' (all bits cleared).
To restate with an example:
\begin{Code}
( ? ) IF 200 ELSE 0 THEN
\end{Code}
is the same as
\begin{Code}
( ? ) 200 and
\end{Code}
Take a look at this example:
\begin{Code}
n a b < IF 45 + THEN
\end{Code}
This phrase either adds 45 to ``$n$'' or doesn't, depending on the
relative sizes of ``$a$'' and ``$b$.'' Since ``adding 45 or not'' is the
same as ``adding 45 or adding 0,'' the difference between the two outcomes
is purely numeric. We can rid ourselves of a decision, and simply
compute:
\begin{Code}
n a b < 45 and +
\end{Code}
\begin{interview}
\person{Moore}:\index{M!Moore, Charles|(}
\begin{tfquot}
The ``\forth{45 AND}'' is faster than the \forth{IF}, and certainly more
graceful. It's simpler. If you form a habit of looking for instances where
you're calculating this value from that value, then usually by doing
arithmetic on the logic you get the same result more cleanly.
I don't know what you call this. It has no terminology; it's merely doing
arithmetic with truth values. But it's perfectly valid, and someday boolean
algebra and arithmetic expressions will accommodate it.
In books you often see a lot of piece-wise linear approximations that fail to
express things clearly. For instance the expression
\begin{Code}[commandchars=\&\{\}]
x = 0 for t < 0
x = 1 for t &(&ge&) 0
\end{Code}
This would be equivalent to
\begin{Code}
t 0< 1 and
\end{Code}
as a single expression, not a piece-wise expression.
\end{tfquot}\index{M!Moore, Charles|)}
\end{interview}%%
\index{A!AND|)}
I call these flags ``hybrid values'' because they are booleans (truth
values) being applied as data (numeric values). Also, I don't know what
else to call them.%
\index{H!Hybrid values|)}
We can eliminate numeric \forth{ELSE} clauses as well (where both results
are non-zero), by factoring out the difference between the two results.
For instance,
\begin{Code}
: STEPPERS 'TESTING? @ IF 150 ELSE 151 THEN load ;
\end{Code}
can be simplified to
\begin{Code}
: STEPPERS 150 'TESTING? @ 1 and + load ;
\end{Code}
This approach works here because conceptually we want to either load
Screen 150, or if testing, the next screen past it.%
\index{C!Calculations|)}%
\index{C!Control structure minimization!calculating results|)}
\section{A Note on Tricks}%
\index{T!Tricks|(}%
\index{C!Control structure minimization!tricks|(}
This sort of approach is often labeled a ``trick.'' In the computing
industry at large, tricks have a bad reputation.
A trick is simply taking advantage of certain properties of operation.
Tricks are used widely in engineering applications. Chimneys
eliminate smoke by taking advantage of the fact that heat rises.
Automobile tires provide traction by taking advantage of gravity.
Arithmetic Logic Units (ALUs) take advantage of the fact that
subtracting a number is the same as adding its two's complement.
These tricks allow simpler, more efficient designs. What justifies
their use is that the assumptions are certain to remain true.
The use of tricks becomes dangerous when a trick depends on something
likely to change, or when the thing it depends on is not protected by
information hiding.
Also, tricks become difficult to read when the assumptions on which
they're based aren't understood or explained. In the case of replacing
conditionals with \forth{AND}, once this technique becomes part of every
programmer's vocabulary, code can become \emph{more} readable. In the case
of a trick that is specific to a specific application, such as the order
in which data are arranged in a table, the listing must clearly document
the assumption used by the trick.%
\index{C!Control structure minimization!tricks|)}%
\index{T!Tricks|)}%
\begin{tip}
Use \forthb{MIN} and \forthb{MAX} for clipping.
\index{M!MAX}%
\index{M!MIN}%
\end{tip}
Suppose we want to decrement the contents of the variable \forth{VALUE}, but
we don't want the value to go below zero:
\begin{Code}
-1 Value +! Value @ -1 = IF 0 Value ! THEN
\end{Code}
This is more simply written:
\begin{Code}
Value @ 1- 0 max Value !
\end{Code}
In this case the conditional is factored within the word \forthb{MAX}.
\subsection{Using Decision Tables}%
\index{D!Decision table|(}%
\index{C!Control structure minimization!decision tables|(}
\begin{tip}
Use decision tables.
\end{tip}
We introduced these in \Chap{2}. A decision table is a structure that
contains either data (a ``data table'') or addresses of functions (a
``function table'') arranged according to any number of dimensions. Each
dimension represents all the possible, mutually exclusive states of a
particular aspect of the problem. At the intersection of the ``true''
states of each dimension lies the desired element: the piece of data or
the function to be performed.
A decision table is clearly a better choice than a conditional structure
when the problem has multiple dimensions.
\subsubsection{One-Dimensional Data Table}
\index{O!One-dimensional data table|(}
Here's an example of a simple, one-dimensional data table. Our application
has a flag called \forth{'FREEWAY?} which is true when we're referring to
freeways, false when we're referring to city streets.
Let's construct the word \forth{SPEED-LIMIT}, which returns the speed
limit depending on the current state.
Using \forthb{IF }\forthb{THEN} we would write:
\begin{Code}
: SPEED-LIMIT ( -- speed-limit)
'FREEWAY? @ IF 55 ELSE 25 THEN ;
\end{Code}
We might eliminate the \forthb{IF }\forthb{THEN} by using a hybrid value
with \forthb{AND}:
\begin{Code}
: SPEED-LIMIT 25 'FREEWAY? @ 30 and + ;
\end{Code}
But this approach doesn't match our conceptual model of the problem
and therefore isn't very readable.
Let's try a data table. This is a one-dimensional table, with only two
elements, so there's not much to it:
\begin{Code}
Create LIMITS 25 , 55 ,
\end{Code}
The word \forth{SPEED-LIMIT?} now must apply the boolean to offset into
the data table:
\begin{Code}
: SPEED-LIMIT ( -- speed-limit)
LIMITS 'FREEWAY? @ 2 and + @ ;
\end{Code}
Have we gained anything over the \forthb{IF }\forthb{THEN} approach?
Probably not, with so simple a problem.
What we have done, though, is to factor out the decision-making
process from the data itself. This becomes more cost-effective when we
have more than one set of data related to the same decision. Suppose we
also had
\begin{Code}
Create #LANES 4 , 10 ,
\end{Code}
representing the number of lanes on a city street and on a freeway. We
can use identical code to compute the current number of lanes:
\begin{Code}
: #LANES? ( -- #lanes)
#LANES 'FREEWAY? @ 2 and + @ ;
\end{Code}
Applying techniques of factoring, we simplify this to:
\begin{Code}
: ROAD ( for-freeway for-city ) Create , ,
DOES> ( -- data ) 'FREEWAY? @ 2 and + @ ;
55 25 ROAD SPEED-LIMIT?
10 4 ROAD #LANES?
\end{Code}
Another example of the one-dimensional data table is the ``superstring''
(\emph{Starting \Forth{}}, Chapter Ten).
\index{O!One-dimensional data table|)}