Decision tables: Difference between revisions

From Rosetta Code
Content added Content deleted
m (J: shorten data lines)
Line 26: Line 26:
'q a'=.x
'q a'=.x
smoutput 'Having trouble? Let''s track down the problem:'
smoutput 'Having trouble? Let''s track down the problem:'
options=. a #~ y {~ #. 'Y'={.@toupper@deb@(1!:1)@1:@smoutput@:,&'?'&dtb"1 q
options=. a #~ y {~ #. 'Y'={.@toupper@deb@(1!:1)@1:@smoutput@,&'?'&dtb"1 q
(,~ ('/'cut'Suggested resolutions:/Solution unknown.') {::~ 0=#) options
(,~ ('/'cut'Suggested resolutions:/Solution unknown.') {::~ 0=#) options
)</lang>
)</lang>

Revision as of 13:29, 31 January 2011

Decision tables is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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.

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.

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

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

  1. http://rosettacode.org/wiki/Keyboard_Input/Obtain_a_Y_or_N_response#Tcl

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