EVALUATE ... ALSO decision tables
Business rules often depend on two or three things at once: customer type
and order size, region and weight, age band and claims history.
Analysts write these as decision tables. EVALUATE ... ALSO lets you
code a decision table almost exactly as it's drawn.
IDENTIFICATION DIVISION.
PROGRAM-ID. DECIDE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CUST-TYPE PIC X VALUE "G".
01 WS-ORDER-VALUE PIC 9(6)V99 VALUE 750.00.
01 WS-MEMBER PIC X VALUE "N".
PROCEDURE DIVISION.
EVALUATE WS-CUST-TYPE ALSO TRUE
WHEN "G" ALSO WS-ORDER-VALUE >= 1000
DISPLAY "GOLD, BIG ORDER: 15% OFF"
WHEN "G" ALSO ANY
DISPLAY "GOLD: 10% OFF"
WHEN "S" ALSO WS-ORDER-VALUE >= 1000
DISPLAY "SILVER, BIG ORDER: 5% OFF"
WHEN OTHER
DISPLAY "NO DISCOUNT"
END-EVALUATE
EVALUATE WS-MEMBER ALSO WS-ORDER-VALUE
WHEN "Y" ALSO ANY
WHEN "N" ALSO 500 THRU 99999.99
DISPLAY "FREE DELIVERY"
WHEN "N" ALSO ANY
DISPLAY "DELIVERY 4.99"
WHEN OTHER
DISPLAY "BAD MEMBER FLAG: " WS-MEMBER
END-EVALUATE
STOP RUN.
Output:
GOLD: 10% OFF
FREE DELIVERY
How ALSO works
EVALUATE WS-CUST-TYPE ALSO TRUE has two subjects. Each WHEN must
supply one object per subject, separated by ALSO, and a WHEN matches
only if every object matches its subject. Read the first EVALUATE as
a table:
| Customer type | Order ≥ 1000? | Discount |
|---|---|---|
| G | yes | 15% |
| G | (any) | 10% |
| S | yes | 5% |
| other | none |
The objects you can use in each position:
- a value or
THRUrange, compared with that subject - a condition when the subject is
TRUE(orFALSE) ANY, which matches anything — a "don't care" cell in the tableNOTbefore a value or range to reverse it
As always, the first matching WHEN wins, so put specific rows before
general ones. Row 2 ("G" ALSO ANY) only catches gold customers because
row 1 has already taken the big orders. Swap them and big gold orders get
10% instead of 15% — no error, just the wrong money.
In the second EVALUATE, two stacked WHENs share one action: members
always get free delivery, and so do non-members spending 500 or more.
WHEN OTHER catches anything unexpected, like a member flag that's neither
Y nor N.
Mind the gaps between ranges
With PIC 9(3)V9, the ranges 0 THRU 2.0 and 2.1 THRU 10.0 cover
every possible value up to 10.0. With PIC 9(3)V99 they'd leave a gap:
2.05 matches neither. Match your range boundaries to the picture's
decimal places.
On the job
Rating engines in insurance and pricing systems are often written as
large EVALUATE ... ALSO ... blocks with dozens of rows, reviewed side
by side with the analyst's spreadsheet. Keeping the code in the same row
order as the spec makes that review — and later audits — much easier.
Your task
A courier's pricing program reads a zone code and a parcel weight
in kg (one decimal, e.g. 2.5), one per line. Postage comes from this
table:
| Zone | Weight (kg) | Postage |
|---|---|---|
D domestic |
0 – 2.0 | 4.50 |
D domestic |
2.1 – 10.0 | 7.95 |
E Europe |
0 – 2.0 | 9.00 |
E Europe |
2.1 – 10.0 | 15.50 |
W world |
0 – 10.0 | 24.00 |
Output for a priced parcel (through WS-POSTAGE-OUT):
POSTAGE: 7.95
A known zone with a weight over 10.0 displays REJECTED: OVER 10 KG. Any
other zone code displays REJECTED: UNKNOWN ZONE.
Write a single EVALUATE WS-ZONE ALSO WS-WEIGHT decision table, using
THRU for the weight bands and ANY for the over-weight rows.