[PYTHON] Avec les types de données algébriques et la programmation orientée objet

Le titre n'est pas clair.

Il y a un essai sur la programmation fonctionnelle (langage).

Je l'ai lu car il y a un excellent article traduit.

[Type de données algébrique] exprimé en OCaml dans cet essai (http://ja.wikipedia.org/wiki/%E4%BB%A3%E6%95%B0%E7%9A%84%E3% 83% 87% E3% 83% BC% E3% 82% BF% E5% 9E% 8B) et des exemples de correspondance de motifs sont présentés.

  • Expression et évaluateur en OCaml
type 'a expr = | True 
               | False 
               | And  of  'a expr * 'a  expr 
               | Or   of  'a expr * 'a  expr 
               | Not  of  'a expr 
               | Base of  'a  
 
let  rec eval eval_base expr  = 
   let  eval' x = eval eval_base x in 
   match expr with 
   | True  -> true 
   | False -> false 
   | Base base  -> eval_base base 
   | And  (x,y) -> eval' x && eval' y  
   | Or  (x,y)  -> eval' x || eval' y 
   | Not  x     -> not (eval' x) 

Il fournit également une comparaison de ce qui se passe lorsque l'équivalent est implémenté en Java. Je pense que de nombreux langages de programmation non fonctionnels ne prennent pas en charge les types de données algébriques (types à somme directe) en tant que langages. Python est l'un de ces langages. Envisagez de remplacer le code Java présenté dans l'article par Python.

Le premier est la mise en œuvre de l'évaluateur. Puisque le code Java de l'article original définit uniquement l'interface et n'introduit pas l'implémentation, nous utilisons également * ABCMeta * et * abstractmethod * pour l'interface, et l'implémentation est appropriée.

3.4


from abc import ABCMeta, abstractmethod

class Evaluator(metaclass=ABCMeta):
    @abstractmethod
    def evaluate(self, value): pass

class MyEvaluator(Evaluator):
    def evaluate(self, value):
        return bool(value)

Passons maintenant à la définition des expressions booléennes et des fonctions qui les évaluent. Comme l '* Evaluator * ci-dessus, il hérite d'un élément de type interface appelé * Expr * et définit chaque expression comme un objet.

3.4


class Expr(metaclass=ABCMeta):
    @abstractmethod
    def eval(self, evaluator): pass

class True_(Expr):
    def eval(self, evaluator):
        return True

class False_(Expr):
    def eval(self, evaluator):
        return False

class Base(Expr):
    def __init__(self, value):
        self.value = value

    def eval(self, evaluator):
        return evaluator.evaluate(self.value)

class And(Expr):
    def __init__(self, expr1, expr2):
        self.expr1 = expr1
        self.expr2 = expr2

    def eval(self, evaluator):
        return self.expr1.eval(evaluator) and self.expr2.eval(evaluator)

class Or(Expr):
    def __init__(self, expr1, expr2):
        self.expr1 = expr1
        self.expr2 = expr2

    def eval(self, evaluator):
        return self.expr1.eval(evaluator) or self.expr2.eval(evaluator)

class Not(Expr):
    def __init__(self, expr):
        self.expr = expr

    def eval(self, evaluator):
        return not self.expr.eval(evaluator)

Est-ce que c'est comme ça?

Laissons de côté les détails et exécutons-le pour voir si cela fonctionne.

3.4


>>> from sample1 import *
>>> evaluator = MyEvaluator()
>>> true, false = True_(), False_()
>>> true.eval(evaluator)
True
>>> And(Base(3), false).eval(evaluator)
False

Je pense que l'appeler comme une fonction * eval * ressemble plus à ça.

3.4


>>> from operator import methodcaller
>>> eval_ = methodcaller('eval', evaluator)
>>> eval_(Not(true))
False
>>> eval_(Or(And(Base(3), false), Not(false)))
True

・ ・ ・

Ce n'est probablement pas très important à faire (je le fais juste pour tester l'implémentation).

J'ai pu exprimer la somme directe des types de données algébriques en utilisant des interfaces (comme des choses) et l'héritage. Regardons maintenant la définition des types de données algébriques dans Ocaml.

type 'a expr = | True 
               | False 
               | And  of  'a expr * 'a  expr 
               | Or   of  'a expr * 'a  expr 
               | Not  of  'a expr 
               | Base of  'a  

Article original(Traduction)Citant de

L'expression de type à somme directe est indiquée par un tube séparant les différentes parties de la déclaration. Parmi ces déclarations, True et False, par exemple, sont des balises uniques et sont pratiquement identiques aux éléments d'énumération Java et C. D'autres, comme And et Not, ont des données liées qui peuvent changer de temps en temps. Ce type contient en fait à la fois le type de somme directe et le type de produit direct car les parties And et Or contiennent des taples. Un type consistant en une combinaison de structures produit et somme multiples est courant dans OCaml et est un idiome puissant.

Plutôt que de l'exprimer dans une classe en utilisant l'héritage**|** (tuyau)Il ne fait aucun doute que l’expression décrite dans est beaucoup plus concise.

De plus, le type somme directe est un type énumération(enum)Mais on dit que cela peut être exprimé, alors essayons cela aussi. Python 3.De 4 à la bibliothèque standardenumDes modules ont été ajoutés. la normeenumParce qu'il ne peut pas être exprimé de manière conciseextenumJ'utilise l'extension, mais ne vous inquiétez pas, ce n'est pas indispensable.

3.4


from extenum import ConstantSpecificEnum

class Expr(ConstantSpecificEnum):

    TRUE = 1
    FALSE = 2
    BASE = 3
    AND = 4
    OR = 5
    NOT = 6

    @overload(TRUE)
    def eval(self, evaluator, *args):
        return True

    @overload(FALSE)
    def eval(self, evaluator, *args):
        return False

    @overload(BASE)
    def eval(self, evaluator, *args):
        return evaluator.evaluate(args[0])

    @overload(AND)
    def eval(self, evaluator, *args):
        return evaluator.evaluate(args[0]) and evaluator.evaluate(args[1])

    @overload(OR)
    def eval(self, evaluator, *args):
        return evaluator.evaluate(args[0]) or evaluator.evaluate(args[1])

    @overload(NOT)
    def eval(self, evaluator, *args):
        return not evaluator.evaluate(args[0])

UnenumPuisque nous avons pu définir un objet équivalent à une expression dans la classe, cela peut être un peu mieux que l'exemple d'héritage précédent.

3.4


>>> from sample2 import *
>>> Expr.TRUE.eval(evaluator)
True
>>> Expr.AND.eval(evaluator,
...               Expr.BASE.eval(evaluator, 3),
...               Expr.FALSE.eval(evaluator))
...
False

Si vous masquez l'évaluateur car il semble redondant,

3.4


>>> true = Expr.TRUE.eval(evaluator)
>>> false = Expr.FALSE.eval(evaluator)

>>> from functools import partial
>>> Base = partial(Expr.BASE.eval, evaluator)
>>> And = partial(Expr.AND.eval, evaluator)
>>> Or = partial(Expr.OR.eval, evaluator)
>>> Not = partial(Expr.NOT.eval, evaluator)

>>> Not(true)
False
>>> Or(And(Base(3), false), Not(false))
True

C'est devenu plus facile de voir comme ça(Il est plus facile de tester si cela fonctionne) 。

Dans le commentaire de Hateb, il y avait un commentaire selon lequel il n'est pas juste d'exprimer et de mettre en contraste les caractéristiques du langage de programmation fonctionnelle avec le langage de programmation procédurale. Je pense que cela a du sens, mais j'en loue un en revanche.(Mépriser)Je pensais plutôt que l'article original était une excellente occasion de réfléchir à la façon dont différents paradigmes de langage de programmation seraient utilisés dans le même but.

Par exemple, je ne suis pas familier avec les langages de programmation fonctionnels, il est donc toujours plus facile de comprendre ce que le code a l'intention de faire en Java qu'en OCaml. Et pour la même raison, je l'ai écrit en Python.

Si l'expression est concise, c'est mieux et la programmation(Langue)J'ai pensé que c'était un bon exemple pour réaliser que le paradigme et l'expressivité du code sont étroitement liés.

Recommended Posts

Avec les types de données algébriques et la programmation orientée objet
Avec les types de données algébriques et FizzBuzz
Avec les types de données algébriques et la correspondance de modèles
Comprendre les types de données et le début de la régression linéaire
Qu'est-ce que la «programmation fonctionnelle» et «orientée objet»? Édition Python
Airflow-I a essayé de programmer le calendrier et de surveiller le pipeline de données
fonctions cv2 et types de données (liaison python OpenCV)
Programmation avec Python et Tkinter
Coordinateur et plan linéaire entier
Modélisation de données point et figure
Extraire les données csv et calculer
Livres et sources recommandés de programmation d'analyse de données (Python ou R)