Skip to content

Instantly share code, notes, and snippets.

@skanev
Created November 8, 2012 08:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save skanev/4037623 to your computer and use it in GitHub Desktop.
Save skanev/4037623 to your computer and use it in GitHub Desktop.
Data-Directed Programming Example Code
operations = {}
def get(name, types):
return operations.get((name,) + types)
def put(name, args, code):
operations[(name,) + args] = code
def apply_generic(name, *args):
types = tuple(map(type, args))
code = get(name, types)
if not code:
raise TypeError('Undefined operation {0} for types {1}'.format(
name, [t.__name__ for t in types]))
return code(*args)
# Types
class Integer:
def __init__(self, n):
self.n = n
def __repr__(self):
return 'Integer({0})'.format(self.n)
def __eq__(self, other):
return self.n == other.n
def gcd(a, b):
if not b:
return a
else:
return gcd(b, b % a)
class Rational:
def __init__(self, num, den):
r = gcd(num, den)
self.num = num / r
self.den = den / r
def __repr__(self):
return 'Rational({0}, {1})'.format(self.num, self.den)
def __eq__(self, other):
return (self.num, self.den) == (other.num, other.den)
# Operations:
def add(a, b):
return apply_generic('add', a, b)
# Data-directed infrastructure
operations = {}
def get(name, types):
return operations.get((name,) + types)
def put(name, args, code):
operations[(name,) + args] = code
def apply_generic(name, *args):
types = tuple(map(type, args))
code = get(name, types)
if not code:
if coercable(args):
return apply_generic(name, *coerce_args(args))
else:
raise TypeError('Undefined operation {0} for types {1}'.format(
name, [t.__name__ for t in types]))
return code(*args)
def coercable(args):
return coerce_args(args) != None
def coerce_args(args):
types = tuple(map(type, args))
if types == (Integer, Rational):
return (Rational(args[0].n, 1), args[1])
elif types == (Rational, Integer):
return (args[0], Rational(args[1].n, 1))
else:
return None
# Operations implementation
put('add', (Integer, Integer), lambda a, b: Integer(a.n + b.n))
put('add', (Rational, Rational),
lambda a, b: Rational(a.num * b.den + b.num * a.den, a.den * b.den))
# Tests
import unittest
class NumbersTest(unittest.TestCase):
def test_equality(self):
self.assertEqual(Integer(1), Integer(1))
self.assertNotEqual(Integer(1), Integer(2))
self.assertEqual(Rational(1, 2), Rational(1, 2))
self.assertEqual(Rational(1, 2), Rational(2, 4))
self.assertNotEqual(Rational(1, 2), Rational(3, 4))
def test_add(self):
self.assertEqual(add(Integer(1), Integer(2)), Integer(3))
self.assertEqual(add(Rational(1, 4), Rational(1, 2)), Rational(3, 4))
def test_coercion(self):
self.assertEqual(add(Rational(1, 4), Integer(1)), Rational(5, 4))
self.assertEqual(add(Integer(1), Rational(1, 4)), Rational(5, 4))
if __name__ == '__main__':
unittest.main()
import generic_operations
# Types
def gcd(a, b):
if not b:
return a
else:
return gcd(b, b % a)
class Number:
def __add__(self, other):
return generic_operations.apply_generic('add', self, other)
class Integer(Number):
def __init__(self, n):
self.n = n
def __repr__(self):
return 'Integer({0})'.format(self.n)
def __eq__(self, other):
return self.n == other.n
class Rational(Number):
def __init__(self, num, den):
r = gcd(num, den)
self.num = num / r
self.den = den / r
def __repr__(self):
return 'Rational({0}, {1})'.format(self.num, self.den)
def __eq__(self, other):
return (self.num, self.den) == (other.num, other.den)
# Operations implementation
generic_operations.put('add', (Integer, Integer), lambda a, b: Integer(a.n + b.n))
generic_operations.put('add', (Rational, Rational),
lambda a, b: Rational(a.num * b.den + b.num * a.den, a.den * b.den))
# Tests
import unittest
class NumbersTest(unittest.TestCase):
def test_equality(self):
self.assertEqual(Integer(1), Integer(1))
self.assertNotEqual(Integer(1), Integer(2))
self.assertEqual(Rational(1, 2), Rational(1, 2))
self.assertEqual(Rational(1, 2), Rational(2, 4))
self.assertNotEqual(Rational(1, 2), Rational(3, 4))
def test_add(self):
self.assertEqual(Integer(1) + Integer(2), Integer(3))
self.assertEqual(Rational(1, 4) + Rational(1, 2), Rational(3, 4))
if __name__ == '__main__':
unittest.main()
PI = 3.1415
class Square:
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
def perimeter(self):
return self.side * 4
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return PI * self.radius ** 2
def perimeter(self):
return 2 * PI * self.radius
# Tests
import unittest
class DataDirectedTest(unittest.TestCase):
def test_circle(self):
self.assertEqual(Circle(3.0).perimeter(), 6.0 * PI)
self.assertEqual(Circle(3.0).area(), 9.0 * PI)
def test_squre(self):
self.assertEqual(Square(3.0).perimeter(), 12.0)
self.assertEqual(Square(3.0).area(), 9.0)
if __name__ == '__main__':
unittest.main()
# Types and operations
PI = 3.1415
class Square:
def __init__(self, side):
self.side = side
class Circle:
def __init__(self, radius):
self.radius = radius
def area(shape):
return apply_generic('area', shape)
def perimeter(shape):
return apply_generic('perimeter', shape)
# Data-directed infrastructure
operations = {}
def get(name, types):
return operations.get((name,) + types)
def put(name, args, code):
operations[(name,) + args] = code
def apply_generic(name, *args):
types = tuple(map(type, args))
code = get(name, types)
if not code:
raise TypeError('Undefined operation {0} for types {1}'.format(
name, [t.__name__ for t in types]))
return code(*args)
# Operations implementation
put('perimeter', (Circle,), lambda c: 2 * PI * c.radius)
put('area', (Circle,), lambda c: PI * c.radius ** 2)
put('perimeter', (Square,), lambda s: s.side * 4.0)
put('area', (Square,), lambda s: s.side ** 2)
# Adding a rectangle
class Rectangle:
def __init__(self, a, b):
self.a, self.b = a, b
put('perimeter', (Rectangle,), lambda r: 2 * r.a + 2 * r.b)
put('area', (Rectangle,), lambda r: r.a * r.b)
# Adding a double
def double(shape):
return apply_generic('double', shape)
SQRT2 = 2 ** 0.5
put('double', (Circle,), lambda c: Circle(c.radius * SQRT2))
put('double', (Square,), lambda s: Square(s.side * SQRT2))
put('double', (Rectangle,), lambda r: Rectangle(r.a * SQRT2, r.b * SQRT2))
# Tests
import unittest
class DataDirectedTest(unittest.TestCase):
def test_circle(self):
self.assertEqual(perimeter(Circle(3.0)), 6.0 * PI)
self.assertEqual(area(Circle(3.0)), 9.0 * PI)
def test_squre(self):
self.assertEqual(perimeter(Square(3.0)), 12.0)
self.assertEqual(area(Square(3.0)), 9.0)
def test_rectangle(self):
self.assertEqual(perimeter(Rectangle(3.0, 5.0)), 16.0)
self.assertEqual(area(Rectangle(3.0, 5.0)), 15.0)
def test_double(self):
self.assertAlmostEqual(area(double(Circle(3.0))), 18.0 * PI)
self.assertAlmostEqual(area(double(Square(3.0))), 18.0)
self.assertAlmostEqual(area(double(Rectangle(3.0, 5.0))), 30.0)
if __name__ == '__main__':
unittest.main()
# Types and operations
import generic_operations
class Figure:
def perimeter(self):
return generic_operations.apply_generic('perimeter', self)
def double(self):
return generic_operations.apply_generic('double', self)
def area(self):
return generic_operations.apply_generic('area', self)
class Square(Figure):
def __init__(self, side):
self.side = side
class Circle(Figure):
def __init__(self, radius):
self.radius = radius
class Rectangle(Figure):
def __init__(self, a, b):
self.a, self.b = a, b
# Operations implementation
SQRT2 = 2 ** 0.5
PI = 3.1415
generic_operations.put('perimeter', (Circle,), lambda c: 2 * PI * c.radius)
generic_operations.put('perimeter', (Square,), lambda s: s.side * 4.0)
generic_operations.put('perimeter', (Rectangle,), lambda r: 2 * r.a + 2 * r.b)
generic_operations.put('area', (Circle,), lambda c: PI * c.radius ** 2)
generic_operations.put('area', (Square,), lambda s: s.side ** 2)
generic_operations.put('area', (Rectangle,), lambda r: r.a * r.b)
generic_operations.put('double', (Circle,), lambda c: Circle(c.radius * SQRT2))
generic_operations.put('double', (Square,), lambda s: Square(s.side * SQRT2))
generic_operations.put('double', (Rectangle,), lambda r: Rectangle(r.a * SQRT2, r.b * SQRT2))
# Tests
import unittest
class DataDirectedTest(unittest.TestCase):
def test_circle(self):
self.assertEqual(Circle(3.0).perimeter(), 6.0 * PI)
self.assertEqual(Circle(3.0).area(), 9.0 * PI)
def test_squre(self):
self.assertEqual(Square(3.0).perimeter(), 12.0)
self.assertEqual(Square(3.0).area(), 9.0)
def test_rectangle(self):
self.assertEqual(Rectangle(3.0, 5.0).perimeter(), 16.0)
self.assertEqual(Rectangle(3.0, 5.0).area(), 15.0)
def test_double(self):
self.assertAlmostEqual(Circle(3.0).double().area(), 18.0 * PI)
self.assertAlmostEqual(Square(3.0).double().area(), 18.0)
self.assertAlmostEqual(Rectangle(3.0, 5.0).double().area(), 30.0)
if __name__ == '__main__':
unittest.main()
PI = 3.1415
class Square:
def __init__(self, side):
self.side = side
class Circle:
def __init__(self, radius):
self.radius = radius
def area(shape):
if isinstance(shape, Circle):
return PI * shape.radius ** 2
elif isinstance(shape, Square):
return shape.side ** 2
def perimeter(shape):
if isinstance(shape, Circle):
return 2 * PI * shape.radius
elif isinstance(shape, Square):
return shape.side * 4
# Tests
import unittest
class DataDirectedTest(unittest.TestCase):
def test_circle(self):
self.assertEqual(perimeter(Circle(3.0)), 6.0 * PI)
self.assertEqual(area(Circle(3.0)), 9.0 * PI)
def test_squre(self):
self.assertEqual(perimeter(Square(3.0)), 12.0)
self.assertEqual(area(Square(3.0)), 9.0)
if __name__ == '__main__':
unittest.main()
watch(%r{^\w+.py$}) { |m| system 'clear'; system "python3 #{m[0]}" }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment