Created
November 8, 2012 08:48
-
-
Save skanev/4037623 to your computer and use it in GitHub Desktop.
Data-Directed Programming Example Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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