Decision tables: Difference between revisions

From Rosetta Code
Content added Content deleted
(Improved and updated D entry)
Line 194: Line 194:


=={{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;


struct Decision {
struct DecisionTable {
alias immutable(byte)[] IBA ;
alias immutable(bool)[] IBA;
immutable string[] conds ;
const string[] conds, actions;
immutable string[] actions ;
immutable IBA[IBA] rules;
immutable IBA[IBA] rules ;


private static immutable(bool[]) growTo(in bool[] b,
this(string[] c, string[] a, byte[][][] q) {
in size_t newLen)
conds = c.idup ;
pure nothrow {
actions = a.idup ;
IBA[IBA] r ;
bool[] result = new bool[newLen];
foreach(p ; q) {
result[0 .. b.length] = b[];
p[0].length = conds.length ;
return assumeUnique(result);
p[1].length = actions.length ;
r[p[0].idup] = p[1].idup ;
}
rules = assumeUnique(r) ;
}
}


string[] test(bool[] tested, string NoMatchMsg = "it is fine :)") {
this(in string[] c, in string[] a, in bool[][][] q)
const pure nothrow {
byte[] testedBytes = array(map!"to!byte(a?1:0)"(tested)) ;
return test(testedBytes, NoMatchMsg) ;
conds = c;
actions = a;
IBA[IBA] r;
foreach (p; q)
r[growTo(p[0], conds.length)] =
growTo(p[1], actions.length);
rules = assumeUnique(r);
}
}

string[] test(byte[] tested, string NoMatchMsg = "it is fine :)") {
string[] test(in bool[] tested,
string[] rightActions ;
in string NoMatchMsg="It is fine :)")
tested.length = conds.length ;
const pure /*nothrow*/ {
auto rule = tested.idup in rules ;
if(rule !is null)
string[] rightActions;
foreach(i, e ; *rule)
auto iTested = growTo(tested, conds.length);
if(e)
if (iTested in rules)
rightActions ~= actions[i] ;
foreach (i, immutable(byte) e; rules[iTested])
if(rightActions.length > 0)
if (e)
return rightActions ;
rightActions ~= actions[i];

return [NoMatchMsg] ;
if (!rightActions.empty)
return rightActions;
return [NoMatchMsg];
}
}


void consult() {
void consult() const {
bool[] query ;
bool[] query;

string answer ;
foreach(c;conds) {
foreach (cond; conds) {
write(c,"? [y=yes/others=no] ") ;
write(cond, "? [y=yes/others=no] ");
readf("%s\n", &answer) ;
string answer = "no";
try {
query ~= (answer.length > 0 && answer.tolower[0..1] == "y") ;
answer = stdin.readln();
} catch (StdioException) {
writeln("no");
}
query ~= !!answer.startsWith('y', 'Y');
}
}

writeln(test(query).join("\n")) ;
test(query).join("\n").writeln();
}
}
}
}


void main() {
void main() {
Decision d = Decision(
enum { F = false, T = true }
const d = DecisionTable(
["Printer is unrecognised",
["Printer is unrecognised",
"A red light is flashing",
"A red light is flashing",
"Printer does not print"],
"Printer does not print"],

["Check the power cable",
["Check the power cable",
"Check the printer-computer cable",
"Check the printer-computer cable",
Line 253: Line 266:
"Check/replace ink",
"Check/replace ink",
"Check for paper jam"],
"Check for paper jam"],

[[[1,0,0],[0,0,1]],
[[0,1,0],[0,0,0,1]],
[[[T, F, F], [F, F, T]],
[[1,1,0],[0,0,1,1]],
[[F, T, F], [F, F, F, T]],
[[0,0,1],[0,0,0,0,1]],
[[T, T, F], [F, F, T, T]],
[[1,0,1],[1,1,1]],
[[F, F, T], [F, F, F, F, T]],
[[0,1,1],[0,0,0,1,1]],
[[T, F, T], [T, T, T]],
[[1,1,1],[0,1,1,1,0]]
[[F, T, T], [F, F, F, T, T]],
[[T, T, T], [F, T, T, T, F]]
]
]
) ;
);

d.consult() ;
d.consult();
}</lang>
}</lang>
{{out}}
Sample output:
<pre>Printer is unrecognised? [y=yes/others=no] y
<pre>Printer is unrecognised? [y=yes/others=no] no
A red light is flashing? [y=yes/others=no] y
A red light is flashing? [y=yes/others=no] no
Printer does not print? [y=yes/others=no] n
Printer does not print? [y=yes/others=no] no
It is fine :)</pre>
Ensure printer software is installed

Check/replace ink</pre>
=={{header|Go}}==
=={{header|Go}}==
Go has no specific support for decision tables, but they can be implemented easily. With a little ingenuity, literal data can be arranged in rows and columns in a way that preserves the visual associations of decision tables. Go has an init function that might be useful for compiling decision tables at program startup. And Go maps allow efficient lookup of actions given conditions.
Go has no specific support for decision tables, but they can be implemented easily. With a little ingenuity, literal data can be arranged in rows and columns in a way that preserves the visual associations of decision tables. Go has an init function that might be useful for compiling decision tables at program startup. And Go maps allow efficient lookup of actions given conditions.

Revision as of 21:44, 2 April 2012

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.

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> That was easy! Now we implement the printer troubleshooting application: <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!

C

With flaky keyboard input:<lang C>#include <stdio.h>

  1. define N_COND 3
  2. define COND_LEN (1 << N_COND)

struct { char *str, *truth;} cond[N_COND] = { {"Printer does not print", "1111...."}, {"A red light is flashing", "11..11.."}, {"Printer is unrecognised", "1.1.1.1."}, }, solu[] = { {"Check the power cable", "..1....."}, {"Check the printer-computer cable", "1.1....."}, {"Ensure printer software is installed","1.1.1.1."}, {"Check/replace ink", "11..11.."}, {"Check for paper jam", ".1.1...."}, };

int main() { int q, ans, c;

for (q = ans = c = 0; q < N_COND; q++) { do { if (c != '\n') printf("%s? ", cond[q].str); c = getchar(); } while (c != 'y' && c != 'n'); ans = (ans << 1) | (c != 'y'); }

if (ans == COND_LEN - 1) printf("\nSo, you don't have a problem then?\n"); else { printf("\nSolutions:\n"); for (q = 0; q < sizeof(solu)/sizeof(solu[0]); q++) if (solu[q].truth[ans] == '1') printf(" %s\n", solu[q].str); } return 0; }</lang>output<lang>Printer does not print? y A red light is flashing? n Printer is unrecognised? y

Solutions:

   Check the power cable
   Check the printer-computer cable
   Ensure printer software is installed</lang>

D

<lang d>import std.stdio, std.algorithm, std.exception, std.string,

      std.array, std.conv;

struct DecisionTable {

   alias immutable(bool)[] IBA;
   const string[] conds,  actions;
   immutable IBA[IBA] rules;
   private static immutable(bool[]) growTo(in bool[] b,
                                           in size_t newLen)
   pure nothrow {
       bool[] result = new bool[newLen];
       result[0 .. b.length] = b[];
       return assumeUnique(result);
   }
   this(in string[] c, in string[] a, in bool[][][] q)
   const pure nothrow {
       conds = c;
       actions = a;
       IBA[IBA] r;
       foreach (p; q)
           r[growTo(p[0], conds.length)] =
               growTo(p[1], actions.length);
       rules = assumeUnique(r);
   }
   string[] test(in bool[] tested,
                 in string NoMatchMsg="It is fine :)")
   const pure /*nothrow*/ {
       string[] rightActions;
       auto iTested = growTo(tested, conds.length);
       if (iTested in rules)
           foreach (i, immutable(byte) e; rules[iTested])
               if (e)
                   rightActions ~= actions[i];
       if (!rightActions.empty)
           return rightActions;
       return [NoMatchMsg];
   }
   void consult() const {
       bool[] query;
       foreach (cond; conds) {
           write(cond, "? [y=yes/others=no] ");
           string answer = "no";
           try {
               answer = stdin.readln();
           } catch (StdioException) {
               writeln("no");
           }
           query ~= !!answer.startsWith('y', 'Y');
       }
       test(query).join("\n").writeln();
   }

}

void main() {

   enum { F = false, T = true }
   const d = DecisionTable(
           ["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"],
            [[[T, F, F], [F, F, T]],
             [[F, T, F], [F, F, F, T]],
             [[T, T, F], [F, F, T, T]],
             [[F, F, T], [F, F, F, F, T]],
             [[T, F, T], [T, T, T]],
             [[F, T, T], [F, F, F, T, T]],
             [[T, T, T], [F, T, T, T, F]]
            ]
       );
   d.consult();

}</lang>

Output:
Printer is unrecognised? [y=yes/others=no] no
A red light is flashing? [y=yes/others=no] no
Printer does not print? [y=yes/others=no] no
It is fine :)

Go

Go has no specific support for decision tables, but they can be implemented easily. With a little ingenuity, literal data can be arranged in rows and columns in a way that preserves the visual associations of decision tables. Go has an init function that might be useful for compiling decision tables at program startup. And Go maps allow efficient lookup of actions given conditions. <lang go>package main

import (

   "errors"
   "fmt"
   "os"

)

type dtText struct {

   rules, text string

}

var ptText = []dtText{

   {"YYYYNNNN", "Printer does not print"},
   {"YYNNYYNN", "A red light is flashing"},
   {"YNYNYNYN", "Printer is unrecognised"},
   {"--------", ""},
   {"  X     ", "Check the power cable"},
   {"X X     ", "Check the printer-computer cable"},
   {"X X X X ", "Ensure printer software is installed"},
   {"XX  XX  ", "Check/replace ink"},
   {" X X    ", "Check for paper jam"},

}

type dtMap map[string][]string

func compileDT(t []dtText) (dtMap, error) {

   if len(t) == 0 {
       return nil, errors.New("Empty decision table")
   }
   var conditions, actions []dtText
   ruleColumns := len(t[0].rules)
   for i, row := range t {
       if len(row.rules) != ruleColumns {
           return nil, errors.New("Inconsistent number of rule columns")
       }
       if len(row.text) == 0 {
           if conditions != nil {
               return nil, errors.New("Multple separator lines")
           }
           if i == 0 {
               return nil, errors.New("No conditions specified")
           }
           if i == len(t)-1 {
               return nil, errors.New("No actions specified")
           }
           conditions = t[:i]
           actions = t[i+1:]
       }
   }
   if conditions == nil {
       return nil, errors.New("Missing separator line")
   }
   m := make(map[string][]string, ruleColumns)
   kb := make([]byte, len(conditions))
   for col := 0; col < ruleColumns; col++ {
       for i, c := range conditions {
           kb[i] = c.rules[col]
       }
       key := string(kb)
       for _, a := range actions {
           if a.rules[col] != ' ' {
               m[key] = append(m[key], a.text)
           }
       }
   }
   return m, nil

}

func init() {

   var err error
   if ptMap, err = compileDT(ptText); err != nil {
       fmt.Println(err)
       os.Exit(1)
   }

}

var ptMap dtMap

func main() {

   for _, a := range ptMap["NYY"] {
       fmt.Println(a)
   }

}</lang> Output:

Ensure printer software is installed
Check/replace ink

Icon and Unicon

<lang Icon>record cond(text,flags) record act(text,flags,aflags)

procedure main()

  DT := [
     cond("Printer does not print",                  "YYYYNNNN"),
     cond("A red light is flashing",		      "YYNNYYNN"),
     cond("Printer is unrecognised",		      "YNYNYNYN"),
     ,                                            # separator 
     act("Check the power cable",		      "NNYNNNNN"),
     act("Check the printer-computer cable",	      "YNYNNNNN"),
     act("Ensure printer software is installed",     "YNYNYNYN"),
     act("Check/replace ink",			      "YYNNYYNN"),
     act("Check for paper jam",		      "NYNYNNNN") ]
     
  every (act := [])[1 to *DT] :=                # empty csets for actions
  ans :=                                        # empty answer cset
  every d := DT[i := 1 to *DT] do {
     /conds := list(*d.flags,)
     case type(d) of {
        "cond":{
           sel := cset(&cset[i])                  # selector for this cond
           ans ++:= ( ask(d.text) == "Y", sel)    # ... add to answer
           every j := 1 to *conds do              # assemble condition flags
              if d.flags[j] == "Y" then conds[j] ++:= sel
           }
        "act":{
           d.aflags := list(*conds,)
           every j := 1 to *conds do {
              if d.flags[j] == "Y" then 
                 d.aflags[j] := conds[j]          # matching conditions
              if d.aflags[j] == ans 
                 then write(d.text)               # matched, write action
              }
           }
        default: write("----------------------------")   # separator 
        }
     }

end

procedure ask(text) repeat {

  writes(text," ? ")
  a := map(trim(read()),&lcase,&ucase)
  case a of { 
     "Y": return a
     "N": fail
     default: write(a," is an invalid response, enter a Y or N, ") 
     }
  }

end</lang>

Sample Output:

Printer does not print ? Y
A red light is flashing ? N
Printer is unrecognised ? N
----------------------------
Check for paper jam

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!

Ruby

<lang ruby>class DecisionTable

 def initialize(conditions, actions)
   @conditions = conditions
   @actions = []
   @rules = []
   actions.each {|action, ruleset| @actions << action; @rules << ruleset} 
 end
 def run
   puts "Conditions:"
   index = ask_conditions
   puts "Actions:"
   results = @rules.each_with_index.inject([]) do |sum, (ruleset, idx)| 
     sum << @actions[idx] if ruleset[index] == 1
     sum
   end
   results << "PC LOAD LETTER" if results.empty?
   results.each {|res| puts "  #{res}"}
   puts ""
 end
 
 private
 def ask_conditions
   answers = @conditions.inject("") {|sum, c| sum + get_response(c)}
   answers.to_i(2)
 end
 def get_response(prompt)
   loop do
     print "  #{prompt}? "
     case STDIN.gets.strip.downcase
     when /^y/ then return "0"
     when /^n/ then return "1"
     end
   end
 end

end

dt = DecisionTable.new(

     [
       "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
     ], 
     [
       ["Check the power cable",                [0,0,1,0,0,0,0,0]],
       ["Check the printer-computer cable",     [1,0,1,0,0,0,0,0]],
       ["Ensure printer software is installed", [1,0,1,0,1,0,1,0]],
       ["Check/replace ink",                    [1,1,0,0,1,1,0,0]],
       ["Check for paper jam",                  [0,1,0,1,0,0,0,0]],
     ]
    )

loop {dt.run}</lang>

Example

Conditions:
  Printer does not print? y
  A red light is flashing? y
  Printer is unrecognised? y
Actions:
  Check the printer-computer cable
  Ensure printer software is installed
  Check/replace ink

Conditions:
  Printer does not print? y
  A red light is flashing? y
  Printer is unrecognised? n
Actions:
  Check/replace ink
  Check for paper jam

Conditions:
  Printer does not print? y
  A red light is flashing? n
  Printer is unrecognised? y
Actions:
  Check the power cable
  Check the printer-computer cable
  Ensure printer software is installed

Conditions:
  Printer does not print? y
  A red light is flashing? n
  Printer is unrecognised? n
Actions:
  Check for paper jam

Conditions:
  Printer does not print? n
  A red light is flashing? y
  Printer is unrecognised? y
Actions:
  Ensure printer software is installed
  Check/replace ink

Conditions:
  Printer does not print? n
  A red light is flashing? y
  Printer is unrecognised? n
Actions:
  Check/replace ink

Conditions:
  Printer does not print? n
  A red light is flashing? n
  Printer is unrecognised? y
Actions:
  Ensure printer software is installed

Conditions:
  Printer does not print? n
  A red light is flashing? n
  Printer is unrecognised? n
Actions:
  PC LOAD LETTER

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