[PYTHON] Avec les types de données algébriques et FizzBuzz

Le titre n'est pas clair.

[PyAlgebraicDataTypes] J'ai joué avec (https://github.com/benanhalt/PyAlgebraicDataTypes). Inspiré du langage Racket [Algebraic Data Types](http://ja.wikipedia.org/wiki/%E4%BB%A3%E6%95 Il semble que ce soit une bibliothèque qui exprime% B0% E7% 9A% 84% E3% 83% 87% E3% 83% BC% E3% 82% BF% E5% 9E% 8B) en Python. Je l'ai essayé parce qu'il était petit et avait l'air bien pour l'apprentissage.

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()

Une liste d'éléments

3.4


>>> Cons(1, Cons(2, Nil()))
Cons(car=1, cdr=Cons(car=2, cdr=Nil()))

Il peut être exprimé en passant * Cons * à * Cons.cdr *. S'il n'y a pas d'élément, vous pouvez exprimer la fin en utilisant * Nil *.

Puisqu'il est difficile d'écrire ces données * List * à la main, définissons une fonction * range_ * qui a l'élément spécifié par l'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())))))

J'ai pu définir mon propre type de * Liste * avec une structure de données de liste.

Maintenant que je sais comment l'utiliser, je vais définir * FizzBuzzList * avec une structure de données comme 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))

Lorsque vous considérez Fizz Buzz comme une structure de données, la logique ressort lorsque vous créez les données. Ici, il est généré par la logique de Fizz Buzz, qui est généralement appelée, mais vous pouvez également penser à la structure de données de Fizz Buzz avec votre propre logique.

[MatchCases] Vous pouvez exprimer des correspondances de modèles en créant un matcher qui hérite (https://github.com/benanhalt/PyAlgebraicDataTypes#match-cases). Faisons correspondre cette structure de données et produisons 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

Comme ça.

3.4


>>> FizzBuzzMatch(fbrange(16, 1))
'1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz, None'

Étant donné que Python est typé dynamiquement, même s'il est écrit dans un style de correspondance de modèle, l'erreur réelle ne peut être détectée qu'au moment de l'exécution, donc même si elle est implémentée avec * isinstance * comme indiqué ci-dessous, cela peut ne pas faire une grande différence dans cet exemple. ..

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

Cependant, l'instruction if crée une dépendance sur l'ordre, donc lorsque le type de données est dérivé et compliqué (par exemple, lorsque le type FizzBuzz hérite du type Fizz et du type Buzz). Je pense que la rigueur de la correspondance de type est efficace.

Python ne prend pas en charge les types de données algébriques (types à somme directe) en tant que langage

Je ne pense pas que ce soit normal d'y penser, donc c'était intéressant de l'essayer et de réaliser la différence de pensée.

Une dernière chose, la logique de Fizz Buzz est apparue lorsque les données ont été générées. J'ai remarqué qu'il est également différent que le résultat de sortie puisse être modifié sans changer le côté de sortie (lors de la correspondance de motif) en changeant la méthode de génération de données.

Recommended Posts

Avec les types de données algébriques et FizzBuzz
Avec les types de données algébriques et la correspondance de modèles
Avec les types de données algébriques et la programmation orientée objet
Comprendre les types de données et le début de la régression linéaire
Variables Python et types de données appris avec la chimio-automatique
fonctions cv2 et types de données (liaison python OpenCV)
Problème FizzBuzz ceci et cela
Extraire les données csv et calculer
Notes sur Python et les types de dictionnaire
Mémo «Chapitre 5 - Dictionnaires et structuration des données»
Hashing de données en R et Python
À propos des données de séries chronologiques et du surentraînement
Extraire les données et les compétences de Pokemon GO Pokemon