The title is unclear.
[PyAlgebraicDataTypes] I played with (https://github.com/benanhalt/PyAlgebraicDataTypes). Inspired by the language Racket [Algebraic Data Types](http://en.wikipedia.org/wiki/%E4%BB%A3%E6%95 It seems to be a library that expresses% B0% E7% 9A% 84% E3% 83% 87% E3% 83% BC% E3% 82% BF% E5% 9E% 8B) in Python. It looked good because it was small for learning, so I tried it.
3.4
>>> from adt import ADT, Require
>>> class List(ADT): pass
...
>>> class Nil(List): pass
...
>>> class Cons(List):
... car = Require(int)
... cdr = Require(List)
3.4
>>> Nil()
Nil()
A list with elements
3.4
>>> Cons(1, Cons(2, Nil()))
Cons(car=1, cdr=Cons(car=2, cdr=Nil()))
It can be expressed by passing * Cons * to * Cons.cdr *. If there is no element, you can express the end by using * Nil *.
It is troublesome to write this * List * data by hand, so let's define a * range_ * function that has the element specified by the argument.
3.4
>>> def range_(until, value=0):
... return Nil() if value == until else Cons(value, range_(until, value + 1))
...
>>> range_(5)
Cons(car=0, cdr=Cons(car=1, cdr=Cons(car=2, cdr=Cons(car=3, cdr=Cons(car=4, cdr=Nil())))))
I was able to define my own * List * type with a list data structure.
Now that I know how to use it, let's define * FizzBuzzList * with a data structure like Fizz Buzz.
3.4
class FizzBuzzList(List):
pass
class Value(FizzBuzzList):
value = Require(int)
class Fizz(FizzBuzzList):
value = Require(int)
class Buzz(FizzBuzzList):
value = Require(int)
class FizzBuzz(FizzBuzzList):
value = Require(int)
fizz = Require(Fizz)
buzz = Require(Buzz)
3.4
def fbrange(until, value=0):
"""
>>> fbrange(6, 1) # doctest: +NORMALIZE_WHITESPACE
Cons(car=Value(value=1), cdr=Cons(car=Value(value=2),
cdr=Cons(car=Fizz(value=3), cdr=Cons(car=Value(value=4),
cdr=Cons(car=Buzz(value=5), cdr=Nil())))))
"""
def make_cons(obj):
return Cons(obj, fbrange(until, obj.value + 1))
if value == until:
return Nil()
elif value % 15 == 0:
return make_cons(FizzBuzz(value, Fizz(value), Buzz(value)))
elif value % 5 == 0:
return make_cons(Buzz(value))
elif value % 3 == 0:
return make_cons(Fizz(value))
return make_cons(Value(value))
When you think of Fizz Buzz as a data structure, the logic comes out when you create the data. Here, it is generated by the logic of Fizz Buzz, which is generally called, but you can also think about the data structure of Fizz Buzz with your own logic.
[MatchCases] You can express pattern matching by creating a matcher that inherits (https://github.com/benanhalt/PyAlgebraicDataTypes#match-cases). Let's pattern match this data structure and output Fizz Buzz.
3.4
from adt import MatchCases
class FizzBuzzMatch(MatchCases):
def value(match: Value):
return value
def fizz(match: Fizz):
return 'fizz'
def buzz(match: Buzz):
return 'buzz'
def fizzbuzz(match: FizzBuzz):
return FizzBuzzMatch(fizz) + FizzBuzzMatch(buzz)
def cons(match: Cons):
return '{}, {}'.format(FizzBuzzMatch(car), FizzBuzzMatch(cdr))
def nil(match: Nil):
return None
Like this.
3.4
>>> FizzBuzzMatch(fbrange(16, 1))
'1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz, None'
Since Python is dynamically typed, it may not make a big difference in this example even if it is implemented with * isinstance * as follows, in the sense that even if it is written like a pattern match, the actual error can be detected only at runtime. ..
3.4
def match_fizzbuzz(fblist):
if isinstance(fblist, Value):
return fblist.value
elif isinstance(fblist, Fizz):
return 'fizz'
elif isinstance(fblist, Buzz):
return 'buzz'
elif isinstance(fblist, FizzBuzz):
return match_fizzbuzz(fblist.fizz) + match_fizzbuzz(fblist.buzz)
elif isinstance(fblist, Cons):
return '{}, {}'.format(
match_fizzbuzz(fblist.car),
match_fizzbuzz(fblist.cdr))
elif isinstance(fblist, Nil):
return None
However, the if statement creates a dependency on the order, so when the data type is derived and complicated (for example, when the FizzBuzz type inherits from the Fizz and Buzz types). I feel that the strictness of type matching is effective.
Python does not support algebraic data types (direct sum types) as a language
I don't think it's normal to think about it, so it was interesting to try it and realize the difference in thinking.
One more thing, the logic of Fizz Buzz came out when the data was generated. I noticed that changing the data generation method makes it possible to change the output result without changing the output side (at the pattern matching).
Recommended Posts