Decision tables: Difference between revisions

From Rosetta Code
Content added Content deleted
(bug fix)
(eliminate overly long lines, and dependence on invisible tab character)
Line 4: Line 4:
=={{header|J}}==
=={{header|J}}==
'''Solution''':<lang j>require'strings'
'''Solution''':<lang j>require'strings'
( , (,.~'_NAMES',L:0~]) ;:'RULES ACTIONS' ) =: , > split&.|:L:1 }.&.> (split~ (<'Actions:') i.&1@:= {."1) }."1 TAB cut&> LF cut noun define
'RULE_NAMES RULES'=: |:':'&cut;._2 noun define
Printer does not print: Y Y Y Y N N N N
Printer troubleshooter:
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
A red light is flashing Y Y N N Y Y N N
Printer is unrecognised: Y N Y N Y N Y N
Printer is unrecognised Y N Y N Y N Y N
Actions:
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
)
)


'ACTION_NAMES ACTIONS'=: |:':'&cut;._2 noun define
boxIdx =: <"1@:(<"0)
Check the power cable: - - X - - - - -
RULE_TABLE =: ACTIONS ( (<,'X') = [ )`([: boxIdx (<,'Y') = ])`(0 $~ (,~ $&2)&({:@$)) } RULES
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
troubleshoot =: verb define
RULE_TABLE troubleshoot~ RULES_NAMES ,&< ACTIONS_NAMES
RULE_TABLE troubleshoot~ RULE_NAMES ,&< ACTION_NAMES
:
:
'R A'=.x
'q a'=.x
smoutput 'Having trouble? Let''s track down the problem:'
smoutput 'Having trouble? Let''s track down the problem:'
> (,~ (<;._2'Suggested resolutions: /Solution unknown./') {~ 0=#) TAB,&.> A #~ y {~ boxIdx (<,'Y')=toupper@:(1!:1)@:1:@:smoutput@:(TAB , ,&'?')&.> R
options=. a #~ y {~ #. 'Y'={.@toupper@deb@(1!:1)@1:@smoutput@:,&'?'&dtb"1 q
(,~ ('/'cut'Suggested resolutions:/Solution unknown.') {::~ 0=#) options
)</lang>
)</lang>


'''Example''' (''solution found''):<lang j> troubleshoot ''
'''Example''' (''solution found''):<lang j> troubleshoot ''
Having trouble? Let's track down the problem:
Having trouble? Let's track down the problem:
Printer does not print?
Printer does not print?
Y
Y
A red light is flashing?
A red light is flashing?
Y
Y
Printer is unrecognised?
Printer is unrecognised?
Y
Y
Suggested resolutions:
Suggested resolutions:
Check the printer-computer cable
Check the printer-computer cable
Ensure printer software is installed
Ensure printer software is installed
Check/replace ink </lang>
Check/replace ink </lang>
'''Example''' (''solution not found''):<lang j> troubleshoot ''
'''Example''' (''solution not found''):<lang j> troubleshoot ''
Having trouble? Let's track down the problem:
Having trouble? Let's track down the problem:
Line 53: Line 54:


=== Comments ===
=== Comments ===
The only interesting line in this solution is the one that assigns <tt>RULE_TABLE</tt>. The rest is mostly ancillary support. This is because the task highlights some freedoms J's multidimensional nature provides to the programmer.
The only interesting line in this solution is the one that assigns <tt>RULE_TABLE</tt>. The rest is mostly ancillary support.

Here, we create an array with as many axes (dimensions) as there are rules (questions), plus one. All axes, except the last, have 2 points (to wit: false, true). The last axis contains a boolean vector indicating which actions to try (or not). Thus, J's multidimensional array is leveraged as a tree, and the user's answers provide a path to a leaf (which is accessed arraywise, i.e. all at once).


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) <tt>troubleshoot</tt> 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.
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) <tt>troubleshoot</tt> 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.

Revision as of 03:58, 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

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