HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Logical functions and truth tables

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
tablestruthandfunctionslogical

Problem

This program goes through 7 common logical functions (AND, OR, NOT, NOR, XOR, XNOR, and NAND) and generates and prints truth tables for them all.

#!/usr/bin/env python2
# coding:utf-8
import operator
import itertools

table = list(itertools.product([False, True], repeat=2))

ops={'AND':  ('∧', (lambda a,b:bool(operator.and_(a,b)))),
     'OR':   ('∨', (lambda a,b:bool(operator.or_(a,b)))),
     'NOT':  ('¬', (lambda a,b:bool(operator.not_(a)))),
     'NOR':  ('↓', (lambda a,b:bool(operator.not_(operator.or_(a,b))))),
     'XOR':  ('↮', (lambda a,b:bool(operator.xor(a,b)))),
     'XNOR': ('↔', (lambda a,b:bool(operator.not_(operator.xor(a,b))))),
     'NAND': ('↑', (lambda a,b:bool(operator.not_(operator.and_(a,b)))))}

pad = lambda x:(str(x) if len(str(x)) == 5 else str(x) + ' ')

for i in ops.keys():
    print i +',',ops[i][0],'\n' + '-'*25

    for j in table:
        print '|', pad(j[0]), '|', pad(j[1]), '|', pad(ops[i][1](j[0],j[1])), '|'

    print '-'*25, '\n'


I don't care much about the speed, but how can I improve the style and follow the PEP guidelines in this program?

Solution

You should make a class. This lets you consolidate the implementation to be consistent, and simplifies a number of the details. You can also implement __str__ and __repr__ to make it easier to print them. Use string formatting for this

class LogicalOperator(object):

    def __init__(self, name, symbol, functor):
        self.name = name
        self.symbol = symbol
        self.functor = functor

    def __call__(self, a, b):
        return bool(functor(a, b))

    def __str__(self):
        return self.name

    def __repr__(self):
        return "{}, {}".format(self.name, self.symbol)


Inheriting from namedtuple makes this easier (thanks to Caridorc for pointing out in the comments).

from collections import namedtuple

class LogicalOperator(namedtuple('LogicalOperator', 'name symbol functor')):
    # no __init__ now
    # rest is unchanged


This way you've separated your concerns. The class encapsulates "what is a logical operator and how do I make it print pretty", and then we use it as necessary to get value and formatting for it.

Then we can still create all of our logical operators, but we do it with classes and a tuple instead of a dict and tuples.

ops = (
    LogicalOperator('AND', '∧', operator.and_),
    LogicalOperator('OR', '∨', operator.or_),
    LogicalOperator('NOT', '¬', lambda a, _: not a),
    LogicalOperator('NOR', '↓', lambda a, b: not (a or b)),
    LogicalOperator('XOR', '↮', operator.xor),
    LogicalOperator('XNOR', '↔', lambda a, b: not (a ^ b)),
    LogicalOperator('NAND', '↑', lambda a, b: not (a and b))
)


A better way to pad strings is to use string.rjust. An even better way is to use the Format Specification Mini-Language.

Then we can put it all together.

if __name__ == '__main__':
    column_width = 25
    table = list(itertools.product([False, True], repeat=2))
    for op in ops:
        print(op)
        print("-"*column_width)
        for left, right in table:
            print("|{:^5}|{:^5}|{:^5}|".format(left, right, op(left, right)))
        print("-"*column_width)


Also, you could use more whitespace throughout. Makes it easier to read.

Code Snippets

class LogicalOperator(object):

    def __init__(self, name, symbol, functor):
        self.name = name
        self.symbol = symbol
        self.functor = functor

    def __call__(self, a, b):
        return bool(functor(a, b))

    def __str__(self):
        return self.name

    def __repr__(self):
        return "{}, {}".format(self.name, self.symbol)
from collections import namedtuple

class LogicalOperator(namedtuple('LogicalOperator', 'name symbol functor')):
    # no __init__ now
    # rest is unchanged
ops = (
    LogicalOperator('AND', '∧', operator.and_),
    LogicalOperator('OR', '∨', operator.or_),
    LogicalOperator('NOT', '¬', lambda a, _: not a),
    LogicalOperator('NOR', '↓', lambda a, b: not (a or b)),
    LogicalOperator('XOR', '↮', operator.xor),
    LogicalOperator('XNOR', '↔', lambda a, b: not (a ^ b)),
    LogicalOperator('NAND', '↑', lambda a, b: not (a and b))
)
if __name__ == '__main__':
    column_width = 25
    table = list(itertools.product([False, True], repeat=2))
    for op in ops:
        print(op)
        print("-"*column_width)
        for left, right in table:
            print("|{:^5}|{:^5}|{:^5}|".format(left, right, op(left, right)))
        print("-"*column_width)

Context

StackExchange Code Review Q#134086, answer score: 5

Revisions (0)

No revisions yet.