21

How can I take a truth table and turn it into a compacted if block?

For instance, let's say I have this truth table where A and B are conditions and x, y and z are possible actions:

A B | x y z
-------------
0 0 | 0 0 1
0 1 | 0 0 1
1 0 | 0 1 0
1 1 | 1 0 0

This could transform into below if block:

if(A)
{
    if(B)
    {
        do(x)
    }
    else
    {
        do(y)
    }
}
else
{
    do(z)
}

This is an easy sample, but I frequently have several conditions that combined in different ways should produce different outputs and it gets hard to figure out the most compacted and elegant way to represent their logic in an if block.

Deduplicator
  • 8,591
  • 5
  • 31
  • 50
Juan
  • 655
  • 6
  • 14
  • 12
    you mean transforming a [Karnaugh map](http://en.wikipedia.org/wiki/Karnaugh) into a ifelse cascade? – ratchet freak Aug 22 '11 at 05:30
  • @ratchet: Seems like I do doesn't it? I didn't know about them before. Will have to do some reading but still and app that would do it for me would be nice, if nothing else, to verify my hand made results. – Juan Aug 22 '11 at 05:34
  • @ratchet you should make it an answer, don't you think ? – Jalayn Aug 22 '11 at 06:55
  • 1
    @jalayn most karnaugh tools are for digital circuitry; those have different heuristics than what the question is about – ratchet freak Aug 22 '11 at 11:20
  • 1
    @jsoldi: The answers you receive will be dependent on which site you ask. If you are seeking comments on a particular code fragment containing some if-then-else blocks, it certainly belongs to [Code review (beta)](http://codereview.stackexchange.com). [Stackoverflow](http://stackoverflow.com) will teach you the tools and techniques. On programmers.SE, people will tell you whether you should/should not be concerned about rewriting logic statements for human understanding, or for faster execution. – rwong Aug 22 '11 at 15:15
  • @jsoldi, your set-up reminds me very much of a hardware state machine, such as this one: http://www.cset.sp.utoledo.edu/eet3350/lesson4.html In general it can set any combination of the wires x, y, and z on or off. But in your case it seems that you want to do just one of the actions. I agree with others that a bunch of if statements are not as readable (or as fast when their number is large) as other forms, such as dictionaries. Also, look into perfect hashing - that method is rather quick. – Job Aug 22 '11 at 21:21
  • I think you are all just getting distracted by the truth table. I just mean it to be an analysis of every possible income -> outcome in an if block, so that then I can optimize / compress it. I was actually writing an if block when I asked this question and figured that turning it into an if block might help me writing it, as well as prevent errors by being forced to consider every possible input. – Juan Aug 23 '11 at 03:26
  • This isn’t a development-methodologies post at all. – Ollie Saunders Sep 03 '11 at 16:06
  • What is the rule for the minimum and maximum number of x,y,z's? –  Jan 02 '12 at 06:32
  • 2
    Tool recommendations are off-topic, but if you change the question into "How can *I* do this?" it will be on-topic. If you do want a software recommendation, you should go to softwarerecs.stackexchange.com. – Kilian Foth Dec 17 '14 at 13:57
  • I think this answer of mine to a similar question could hepl: http://programmers.stackexchange.com/questions/205803/how-to-tackle-a-branched-arrow-head-anti-pattern – Tulains Córdova Dec 18 '14 at 18:06
  • How inherent in your problem is it that the number of tasks performed is always exactly one, or always at most one? – gnasher729 Aug 04 '23 at 16:25

9 Answers9

15

If you are designing from a Karnaugh map, then the code may as well look that way too:

//                   a      b
def actionMap = [ false: [false: { z() },
                          true:  { z() }],
                  true:  [false: { x() },
                          true:  { y() }]]

actionMap[a][b]()
kevin cline
  • 33,608
  • 3
  • 71
  • 142
4

In C#.NET, you can use a Dictionary Class to get the result without any IF ELSE as follows - The nice thing about this is:

  1. It is readable
  2. New keys will be unique (otherwise, you get an error)
  3. Sequence does not matter
  4. Easy to add or remove entries

If you don't have an equivalent of Dictionary Class, you can do the same in a binary look-up/search function.

//A B | x y z
//-------------
//0 0 | 0 0 1
//0 1 | 0 0 1
//1 0 | 0 1 0
//1 1 | 1 0 0
// Create a Dictionary object and populate it
Dictionary<string, string> _decisionTable = new Dictionary<string, string>() { 
    { "0,0", "0,0,1" }, 
    { "0,1", "0,0,1" }, 
    { "1,0", "0,1,0" }, 
    { "1,1", "1,0,0"} 
};

//usage example: Find the values of X,Y,Z for A=1,B=0
Console.WriteLine(_decisionTable["1,0"]);
Console.Read();
Glorfindel
  • 3,137
  • 6
  • 25
  • 33
NoChance
  • 12,412
  • 1
  • 22
  • 39
  • 3
    I like this solution, the only change I would make would be to use a Dictionary, Tuple instead of a Dictionary. Then you don't need to build a string for looking up and deconstruct the result afterwards as Tuples will do this for you. – Lyise Dec 18 '14 at 10:09
  • @Lyise, Thank you for your remark. You are absolutely correct. I should incorporate your good point when I get a chance. – NoChance Dec 18 '14 at 10:16
3

What you want is a Rete algorithm. This automatically combs a set of rules and prioritizes them into a tree the way you describe.

There are a number of commercial "rules engine" systems that do this on the very large scale (millions of rules) where execution speed is essential.

Alex Feinman
  • 5,792
  • 2
  • 27
  • 48
2

Here is your library :) And you dont need to pass full K-table, only fields that you are interested in :) It assumes that its AND operator in truth table. If you want to use more operators, you should be able to rewrite it. You can have any number of arguments. Written in python, and tested.

def x():
    print "xxxx"

def y():
    print "yyyyy"

def z(): #this is default function
    print "zzzzz"

def A():
    return 3 == 1

def B():
    return 2 == 2


def insert(statements,function):
    rows.append({ "statements":statements, "function":function })

def execute():
    for row in rows:
        print "==============="
        flag = 1

        for index, val in enumerate(row["statements"]):
            #for first pass of lopp, index is 0, for second its 1....
            #if any function returns result different than one in our row, 
            # we wont execute funtion of that row (mark row as not executable)
            if funcs[index]() != val:
                flag = 0

        if flag == 1:
            #we execute function 
            row["function"]()
        else: z() #we call default function


funcs = [A,B]  #so we can access functions by index key
rows = []

insert( (0,0), y)
insert( (0,1), y)
insert( (1,0), x)
insert( (1,1), x)
insert( (0,1), x)

execute()
Deduplicator
  • 8,591
  • 5
  • 31
  • 50
grizwako
  • 697
  • 5
  • 8
2

Map the inputs into a single value and then switch on it:

#define X(a, b) (!!(a) * 2 + !!(b))
switch(X(A, B)) {
case X(0, 0):
    ...
    break;
case X(0, 1):
    ...
    break;
case X(1, 0):
    ...
    break;
case X(1, 1):
    ...
    break;
}
#undef X
Deduplicator
  • 8,591
  • 5
  • 31
  • 50
  • (a) Somewhat ironically given your username, this will yield significantly duplicated code when the handling of each individual output param should be reusable; which is pretty much what the question title is focusing on. (b) You're reinventing the flags enum. Not sure which language you were using and if it contains an existing implementation of a flags enum, but it's worth mentioning the concept here either to use it or be able to find more documentation on it. – Flater Aug 03 '23 at 01:25
1

A lookup table containing functions pointers can work well in some situations. In C, for example, you can do something like this:

typedef void(*VoidFunc)(void);

void do(int a, int b)
{
    static VoidFunc myFunctions[4] = {z, z, y, x}; // the lookup table

    VoidFunc theFunction = myFunctions[ a * 2 + b ];
    theFunction();
}

This is a good solution when the number of inputs is relatively small, since the number of entries in the table has to be 2^^n where n is the number of inputs. 7 or 8 inputs might be manageable, 10 or 12 starts to get ugly. If you have that many inputs, try to simplify by other means (such as Karnaugh maps) first.

Deduplicator
  • 8,591
  • 5
  • 31
  • 50
Caleb
  • 38,959
  • 8
  • 94
  • 152
1

The best way to express the code depends on what a, b, and x, y, and z are. Sure, in some cases, it may be appropriate to adopt one of the lookup table approaches given in other answers however, in many cases it results in harder to read code, and you'd be best writing code like this:

def f(a, b) :
  if (not a):
    z()
    return

  if (b):
    x()
  else:
    y()

or even:

def f(a, b) :
  if (not a):
    z()
    return

  if (b):
    x()
    return
  
  y()

Frankly, thinking about this in terms of a truth table at all is often the wrong way of doing it. You want to think about the flow of code and what the different options mean and how they are logically associated with each other.

Jack Aidley
  • 2,954
  • 1
  • 15
  • 18
0

Look at the "Gorgeous Karnaugh" software - it can accept truth tables quite exact as your sample, accept analytic boolean formulas definition, accept Lua scripting to build truth tables. Next, "Gorgeous Karnaugh" software draws the K-Maps for taken input, which you can minimize manually or using "Espresso" logic minimizer, and produces output for C/C++ and some hardware languages. Look to the summary features page for "Gorgeous Karnaugh" - http://purefractalsolutions.com/show.php?a=xgk/gkm

Petruchio
  • 9
  • 1
  • This look a lot like what I need, but couldn't get the C/C++ code to show anything other than empty `if`s after entering a truth table. – Juan Jan 02 '12 at 06:27
  • Yes, that tool designed for logic function minimizations - after entering truth table you need to minimize logic function (PoS/SoP - by 0/by 1). The C++ code can be found in the "Minimization results" window after minimization. – Petruchio Jan 04 '12 at 11:42
0

In a language with pattern matching, you don't need to transform anything, because you can code up your table more or less directly. For example in Rust:

match (a, b) {
    (false, _) => z(),
    (true, false) => y(),
    (true, true) => x(),
}

The pros of this approach is that is very efficient, the compiler checks that all cases are covered and only one of the branches is evaluated.

corvus_192
  • 389
  • 2
  • 6