Decision tables: Difference between revisions
m Alphabetize |
No edit summary |
||
Line 2: | Line 2: | ||
[[wp:Decision_table|Decision Tables]] are a precise yet compact way to model complicated logic. Demonstrate how your language implements decision tables. Use the example of Printer Troubleshooting given in the Wikipedia article. |
[[wp:Decision_table|Decision Tables]] are a precise yet compact way to model complicated logic. Demonstrate how your language implements decision tables. Use the example of Printer Troubleshooting given in the Wikipedia article. |
||
=={{header|Ada}}== |
|||
First the specification of a generic decision table package: |
|||
<lang Ada>generic |
|||
type Condition is (<>); |
|||
type Action is (<>); |
|||
with function Ask_Question (Cond: Condition) return Boolean; |
|||
with procedure Give_Answer (Act: Action); |
|||
with procedure No_Answer; |
|||
package Generic_Decision_Table is |
|||
type Answers is array(Condition) of Boolean; |
|||
type Rule_R is record |
|||
If_Then: Answers; |
|||
Act: Action; |
|||
end record; |
|||
type Rule_A is array(Positive range <>) of Rule_R; |
|||
procedure React(Rules: Rule_A); |
|||
end Generic_Decision_Table; |
|||
</lang> |
|||
Next, the implementation of the generic decision table package: |
|||
<lang Ada>package body Generic_Decision_Table is |
|||
procedure React(Rules: Rule_A) is |
|||
A: Answers; |
|||
Some_Answer: Boolean := False; |
|||
begin |
|||
for C in A'Range loop |
|||
A(C) := Ask_Question(C); |
|||
end loop; |
|||
for R in Rules'Range loop |
|||
if A = Rules(R).If_Then then |
|||
Give_Answer(Rules(R).Act); |
|||
Some_Answer := True; |
|||
end if; |
|||
end loop; |
|||
if not Some_Answer then |
|||
No_Answer; |
|||
end if; |
|||
end React; |
|||
end Generic_Decision_Table; |
|||
</lang> |
|||
Finally, the Printer Troubleshooting program, using the generic decision table: |
|||
<lang Ada>with Generic_Decision_Table, Ada.Text_IO; |
|||
procedure Printer_Decision_Table is |
|||
type Condition is (Does_Not_Print, Red_Light_Flashing, Unrecognised); |
|||
type Action is (Power_Cable, Printer_Computer_Cable, Software_Installed, |
|||
New_Ink, Paper_Jam); |
|||
function Question(Cond: Condition) return Boolean is |
|||
use Ada.Text_IO; |
|||
Ch: Character; |
|||
begin |
|||
case Cond is |
|||
when Does_Not_Print => |
|||
Put("Printer does not print?"); |
|||
when Red_Light_Flashing => |
|||
Put("A red light is flashing?"); |
|||
when Unrecognised => |
|||
Put("Printer is unrecognised?"); |
|||
end case; |
|||
Put_Line (" y/Y => 'yes', any other input: 'no'"); |
|||
Get(Ch); |
|||
return (Ch='y') or (Ch='Y'); |
|||
end Question; |
|||
procedure Answer(Act: Action) is |
|||
use Ada.Text_IO; |
|||
begin |
|||
case Act is |
|||
when Power_Cable => |
|||
Put_Line("Check the power cable!"); |
|||
when Printer_Computer_Cable => |
|||
Put_Line("Check the printer-computer cable!"); |
|||
when Software_Installed => |
|||
Put_Line("Ensure the printer software is installed!"); |
|||
when New_Ink => |
|||
Put_Line("Check/replace ink!"); |
|||
when Paper_Jam => |
|||
Put_Line("Check for paper jam!"); |
|||
end case; |
|||
end Answer; |
|||
procedure No_Answer is |
|||
begin |
|||
Ada.Text_IO.Put_Line("Sorry! I have no idea what to do now!"); |
|||
end No_Answer; |
|||
package DT is new Generic_Decision_Table |
|||
(Condition, Action, Question, Answer, No_Answer); |
|||
R: DT.Rule_A := (((True, False, True), Power_Cable), |
|||
((True, True, True), Printer_Computer_Cable), |
|||
((True, False, True), Printer_Computer_Cable), |
|||
((True, True, True), Software_Installed), |
|||
((True, False, True), Software_Installed), |
|||
((False, True, True), Software_Installed), |
|||
((False, False, True), Software_Installed), |
|||
((True, True, True), New_Ink), |
|||
((True, True, False), New_Ink), |
|||
((False, True, True), New_Ink), |
|||
((False, True, False), New_Ink), |
|||
((True, True, False), Paper_Jam), |
|||
((True, False, False), Paper_Jam) |
|||
); |
|||
begin |
|||
DT.React(R); |
|||
end Printer_Decision_Table;</lang> |
|||
Sample output: |
|||
<pre>> ./printer_decision_table |
|||
Printer does not print? y/Y => 'yes', any other input: 'no' |
|||
y |
|||
A red light is flashing? y/Y => 'yes', any other input: 'no' |
|||
n |
|||
Printer is unrecognised? y/Y => 'yes', any other input: 'no' |
|||
n |
|||
Check for paper jam! |
|||
> ./printer_decision_table |
|||
Printer does not print? y/Y => 'yes', any other input: 'no' |
|||
y |
|||
A red light is flashing? y/Y => 'yes', any other input: 'no' |
|||
y |
|||
Printer is unrecognised? y/Y => 'yes', any other input: 'no' |
|||
n |
|||
Check/replace ink! |
|||
Check for paper jam! |
|||
> ./printer_decision_table |
|||
Printer does not print? y/Y => 'yes', any other input: 'no' |
|||
n |
|||
A red light is flashing? y/Y => 'yes', any other input: 'no' |
|||
n |
|||
Printer is unrecognised? y/Y => 'yes', any other input: 'no' |
|||
n |
|||
Sorry! I have no idea what to do now!</pre> |
|||
=={{header|D}}== |
=={{header|D}}== |
||
<lang d>import std.stdio, std.algorithm, std.exception, std.string, std.array, std.conv ; |
<lang d>import std.stdio, std.algorithm, std.exception, std.string, std.array, std.conv ; |
Revision as of 09:31, 30 May 2011
Decision Tables are a precise yet compact way to model complicated logic. Demonstrate how your language implements decision tables. Use the example of Printer Troubleshooting given in the Wikipedia article.
Ada
First the specification of a generic decision table package: <lang Ada>generic
type Condition is (<>); type Action is (<>); with function Ask_Question (Cond: Condition) return Boolean; with procedure Give_Answer (Act: Action); with procedure No_Answer;
package Generic_Decision_Table is
type Answers is array(Condition) of Boolean; type Rule_R is record If_Then: Answers; Act: Action; end record; type Rule_A is array(Positive range <>) of Rule_R;
procedure React(Rules: Rule_A);
end Generic_Decision_Table; </lang> Next, the implementation of the generic decision table package: <lang Ada>package body Generic_Decision_Table is
procedure React(Rules: Rule_A) is A: Answers; Some_Answer: Boolean := False; begin for C in A'Range loop A(C) := Ask_Question(C); end loop; for R in Rules'Range loop if A = Rules(R).If_Then then Give_Answer(Rules(R).Act); Some_Answer := True; end if; end loop; if not Some_Answer then No_Answer; end if; end React;
end Generic_Decision_Table; </lang> Finally, the Printer Troubleshooting program, using the generic decision table: <lang Ada>with Generic_Decision_Table, Ada.Text_IO;
procedure Printer_Decision_Table is
type Condition is (Does_Not_Print, Red_Light_Flashing, Unrecognised); type Action is (Power_Cable, Printer_Computer_Cable, Software_Installed, New_Ink, Paper_Jam);
function Question(Cond: Condition) return Boolean is use Ada.Text_IO; Ch: Character; begin case Cond is when Does_Not_Print => Put("Printer does not print?"); when Red_Light_Flashing => Put("A red light is flashing?"); when Unrecognised => Put("Printer is unrecognised?"); end case; Put_Line (" y/Y => 'yes', any other input: 'no'"); Get(Ch); return (Ch='y') or (Ch='Y'); end Question;
procedure Answer(Act: Action) is use Ada.Text_IO; begin case Act is when Power_Cable => Put_Line("Check the power cable!"); when Printer_Computer_Cable => Put_Line("Check the printer-computer cable!"); when Software_Installed => Put_Line("Ensure the printer software is installed!"); when New_Ink => Put_Line("Check/replace ink!"); when Paper_Jam => Put_Line("Check for paper jam!"); end case; end Answer;
procedure No_Answer is begin Ada.Text_IO.Put_Line("Sorry! I have no idea what to do now!"); end No_Answer;
package DT is new Generic_Decision_Table (Condition, Action, Question, Answer, No_Answer);
R: DT.Rule_A := (((True, False, True), Power_Cable), ((True, True, True), Printer_Computer_Cable), ((True, False, True), Printer_Computer_Cable), ((True, True, True), Software_Installed), ((True, False, True), Software_Installed), ((False, True, True), Software_Installed), ((False, False, True), Software_Installed), ((True, True, True), New_Ink), ((True, True, False), New_Ink), ((False, True, True), New_Ink), ((False, True, False), New_Ink), ((True, True, False), Paper_Jam), ((True, False, False), Paper_Jam) );
begin
DT.React(R);
end Printer_Decision_Table;</lang> Sample output:
> ./printer_decision_table Printer does not print? y/Y => 'yes', any other input: 'no' y A red light is flashing? y/Y => 'yes', any other input: 'no' n Printer is unrecognised? y/Y => 'yes', any other input: 'no' n Check for paper jam! > ./printer_decision_table Printer does not print? y/Y => 'yes', any other input: 'no' y A red light is flashing? y/Y => 'yes', any other input: 'no' y Printer is unrecognised? y/Y => 'yes', any other input: 'no' n Check/replace ink! Check for paper jam! > ./printer_decision_table Printer does not print? y/Y => 'yes', any other input: 'no' n A red light is flashing? y/Y => 'yes', any other input: 'no' n Printer is unrecognised? y/Y => 'yes', any other input: 'no' n Sorry! I have no idea what to do now!
D
<lang d>import std.stdio, std.algorithm, std.exception, std.string, std.array, std.conv ;
struct Decision {
alias immutable(byte)[] IBA ; immutable string[] conds ; immutable string[] actions ; immutable IBA[IBA] rules ;
this(string[] c, string[] a, byte[][][] q) { conds = c.idup ; actions = a.idup ; IBA[IBA] r ; foreach(p ; q) { p[0].length = conds.length ; p[1].length = actions.length ; r[p[0].idup] = p[1].idup ; } rules = assumeUnique(r) ; }
string[] test(bool[] tested, string NoMatchMsg = "it is fine :)") { byte[] testedBytes = array(map!"to!byte(a?1:0)"(tested)) ; return test(testedBytes, NoMatchMsg) ; } string[] test(byte[] tested, string NoMatchMsg = "it is fine :)") { string[] rightActions ; tested.length = conds.length ; auto rule = tested.idup in rules ; if(rule !is null) foreach(i, e ; *rule) if(e) rightActions ~= actions[i] ; if(rightActions.length > 0) return rightActions ; return [NoMatchMsg] ; }
void consult() { bool[] query ; string answer ; foreach(c;conds) { write(c,"? [y=yes/others=no] ") ; readf("%s\n", &answer) ; query ~= (answer.length > 0 && answer.tolower[0..1] == "y") ; } writeln(test(query).join("\n")) ; }
}
void main() {
Decision d = Decision( ["Printer is unrecognised", "A red light is flashing", "Printer does not print"], ["Check the power cable", "Check the printer-computer cable", "Ensure printer software is installed", "Check/replace ink", "Check for paper jam"], [[[1,0,0],[0,0,1]], [[0,1,0],[0,0,0,1]], [[1,1,0],[0,0,1,1]], [[0,0,1],[0,0,0,0,1]], [[1,0,1],[1,1,1]], [[0,1,1],[0,0,0,1,1]], [[1,1,1],[0,1,1,1,0]] ] ) ; d.consult() ;
}</lang> Sample output:
Printer is unrecognised? [y=yes/others=no] y A red light is flashing? [y=yes/others=no] y Printer does not print? [y=yes/others=no] n Ensure printer software is installed Check/replace ink
J
Solution:<lang j>require'strings' 'RULE_NAMES RULES'=: |:':'&cut;._2 noun define
Printer does not print: Y Y Y Y N N N N A red light is flashing: Y Y N N Y Y N N Printer is unrecognised: Y N Y N Y N Y N
)
'ACTION_NAMES ACTIONS'=: |:':'&cut;._2 noun define
Check the power cable: - - X - - - - - Check the printer-computer cable: X - X - - - - - Ensure printer software is installed: X - X - X - X - Check/replace ink: X X - - X X - - Check for paper jam: - X - X - - - -
)
assert (-:~.)|: 'Y' =/&;: RULES RULE_TABLE=: (,/'X'=/&;: ACTIONS) /:&|: 'Y' =/&;: RULES
troubleshoot =: verb define
RULE_TABLE troubleshoot~ RULE_NAMES ,&< ACTION_NAMES
'q a'=.x smoutput 'Having trouble? Lets track down the problem:' options=. a #~ y {~ #. 'Y'={.@toupper@deb@(1!:1)@1:@smoutput@,&'?'@dtb"1 q (,~ ('/'cut'Suggested resolutions:/Solution unknown.') {::~ 0=#) options
)</lang>
Example (solution found):<lang j> troubleshoot Having trouble? Let's track down the problem:
Printer does not print?
Y
A red light is flashing?
Y
Printer is unrecognised?
Y Suggested resolutions:
Check the printer-computer cable Ensure printer software is installed Check/replace ink </lang>
Example (solution not found):<lang j> troubleshoot Having trouble? Let's track down the problem: Printer does not print? N A red light is flashing? N Printer is unrecognised? N Solution unknown. </lang>
Comments
The only interesting line in this solution is the one that assigns RULE_TABLE. The rest is mostly ancillary support.
For large numbers of rules and few actions, J's native support of sparse arrays might provide a performance advantage, particularly in space. A minor note about the implementation here: the verb (function) troubleshoot is generalized, and reusable on any set of rule table, questions, and suggested actions. The default is to use those given in the printer troubleshooting table.
PicoLisp
We allow ourselves a luxurious user interface: <lang PicoLisp>(de yes? (Cond)
(out NIL (prin (car Cond) "? ")) (in NIL (use Reply (loop (setq Reply (read)) (T (member Reply '(T Y YES Yes y yes true 1)) T ) (T (member Reply '(NIL N NO No n no false 0))) (prinl "Please answer 'Yes' or 'No'") ) ) ) )</lang>
The decision table used in the example: <lang PicoLisp>(de *Conditions
("Printer does not print" T T T T NIL NIL NIL NIL) ("A red light is flashing" T T NIL NIL T T NIL NIL) ("Printer is unrecognised" T NIL T NIL T NIL T NIL) )
(de *Actions
("Check the power cable" NIL NIL T) ("Check the printer-computer cable" T NIL T) ("Ensure printer software is installed" T NIL T NIL T NIL T) ("Check/replace ink" T T NIL NIL T T) ("Check for paper jam" NIL T NIL T) )</lang>
The decision can be made directly on the condition and action data, without the need to create intermediate tables: <lang PicoLisp>(de decide ()
(let Reply (mapcar yes? *Conditions) (extract and (apply pick (append *Conditions *Actions) '(@ (unless (pick '((Flg) (<> Flg (next))) Reply) (rest) ) ) ) (mapcar car *Actions) ) ) )</lang>
Output:
: (decide) Printer does not print? y A red light is flashing? y Printer is unrecognised? n -> ("Check/replace ink" "Check for paper jam") : (decide) Printer does not print? n A red light is flashing? y Printer is unrecognised? y -> ("Ensure printer software is installed" "Check/replace ink") : (decide) Printer does not print? n A red light is flashing? n Printer is unrecognised? n -> NIL
Python
<lang python> Create a Decision table then use it
def dt_creator():
print("\n\nCREATING THE DECISION TABLE\n") conditions = input("Input conditions, in order, separated by commas: ") conditions = [c.strip() for c in conditions.split(',')] print( ("\nThat was %s conditions:\n " % len(conditions)) + '\n '.join("%i: %s" % x for x in enumerate(conditions, 1)) ) print("\nInput an action, a semicolon, then a list of tuples of rules that trigger it. End with a blank line") action2rules, action = [], ' ' while action: action = input("%i: " % (len(action2rules) + 1)).strip() if action: name, _, rules = [x.strip() for x in action.partition(';')] rules = eval(rules) assert all(len(rule) == len(conditions) for rule in rules), \ "The number of conditions in a rule to trigger this action is wrong" action2rules.append((name, rules)) actions = [x[0] for x in action2rules] # Map condition to actions rule2actions = dict((y,[]) for y in set(sum((x[1] for x in action2rules), []))) for action, rules in action2rules: for r in rules: rule2actions[r].append( action ) return conditions, rule2actions
def dt_user(dt, default=['Pray!']):
conditions, rule2actions = dt print("\n\nUSING THE DECISION TABLE\n") rule = tuple(int('y' == input("%s? (Answer y if statement is true or n): " % c)) for c in conditions) print("Try this:\n " + '\n '.join(rule2actions.get(rule, default)))
if __name__ == '__main__':
dt = dt_creator() dt_user(dt) dt_user(dt) dt_user(dt)</lang>
Sample Run
CREATING THE DECISION TABLE Input conditions, in order, separated by commas: Printer does not print, A red light is flashing, Printer is unrecognised That was 3 conditions: 1: Printer does not print 2: A red light is flashing 3: Printer is unrecognised Input an action, a semicolon, then a list of tuples of rules that trigger it. End with a blank line 1: Check the power cable; [(1,0,1)] 2: Check the printer-computer cable; [(1,1,1), (1,0,1)] 3: Ensure printer software is installed; [(1,1,1), (1,0,1), (0,1,1), (0,0,1)] 4: Check/replace ink; [(1,1,1), (1,1,0), (0,1,1), (0,1,0)] 5: Check for paper jam; [(1,1,0), (1,0,0)] 6: USING THE DECISION TABLE Printer does not print? (Answer y if statement is true or n): n A red light is flashing? (Answer y if statement is true or n): y Printer is unrecognised? (Answer y if statement is true or n): y Try this: Ensure printer software is installed Check/replace ink USING THE DECISION TABLE Printer does not print? (Answer y if statement is true or n): y A red light is flashing? (Answer y if statement is true or n): n Printer is unrecognised? (Answer y if statement is true or n): y Try this: Check the power cable Check the printer-computer cable Ensure printer software is installed USING THE DECISION TABLE Printer does not print? (Answer y if statement is true or n): n A red light is flashing? (Answer y if statement is true or n): n Printer is unrecognised? (Answer y if statement is true or n): n Try this: Pray!
Tcl
<lang tcl>package require TclOO
proc yesno Template:Message "Press Y or N to continue" {
fconfigure stdin -blocking 0 exec stty raw read stdin ; # flush puts -nonewline "${message}: " flush stdout while {![eof stdin]} { set c [string tolower [read stdin 1]] if {$c eq "y" || $c eq "n"} break } puts [string toupper $c] exec stty -raw fconfigure stdin -blocking 1 return [expr {$c eq "y"}]
}
oo::class create DecisionTable {
variable qlist responses constructor {questions responseMap} {
set qlist $questions set responses $responseMap
}
method consult {} {
set idx 0 foreach q $qlist { set answer [yesno "$q? \[y/n\]"] set idx [expr {$idx*2 + (1-$answer)}] } foreach {msg map} $responses { # Allow a column to be omitted; magic! if {"0[lindex $map $idx]"} { puts $msg } }
}
}</lang> Demonstration: <lang tcl>DecisionTable create printerDiagnosisTable {
"Printer does not print" "A red light is flashing" "Printer is unrecognised"
} {
"Check the power cable" {0 0 1} "Check the printer-computer cable" {1 0 1} "Ensure printer software is installed" {1 0 1 0 1 0 1} "Check/replace ink" {1 1 0 0 1 1} "Check for paper jam" {0 1 0 1}
} printerDiagnosisTable consult</lang> Output:
Printer does not print? [y/n]: N A red light is flashing? [y/n]: Y Printer is unrecognised? [y/n]: Y Ensure printer software is installed Check/replace ink