Monade d'État en Python

Je voudrais utiliser Python. Et quand il s'agit de monades, Maybe est célèbre.

Peut-être est-il relativement facile à comprendre et il est facile de le mettre en pratique dans différentes langues, de sorte que certaines personnes l'implémentent par eux-mêmes. Je pense que ça va arriver. Mais, Je pense qu'il vaut mieux comprendre de la monade d'État J'ai trouvé un article comme celui-là.

Je vais donc mettre en place la monade d'État. Faisons une calculatrice pour l'addition, la soustraction, la multiplication et la division en utilisant stack avec Haskell Monade d'État de Haskell (1) - État d'imitation 6ème monade d'État pour l'utilisation de "l'État" local J'ai lu quelque chose comme ça et j'ai augmenté ma conscience.

Je ne peux pas nier le sentiment qu'il y a tant d'articles existants. En outre, l'explication comprenant l'article de référence C'est concret et facile à comprendre. Cependant, pour cette raison, dans la partie implémentation de la pile Ikansen J'y ai mis trop d'efforts et j'ai senti qu'il était difficile de saisir l'essence de l'État.

Donc, en utilisant un langage légèrement différent et en le réimplémentant vous-même, en tant que mécanisme plus abstrait Cherchons à saisir la monade d'État.

Définition en Haskell

Donc, ici, je ferai référence à l'implémentation Haskell sans penser à rien de plus. Pour le moment, j'aimerais apprendre l'essentiel de la monade d'État en la portant en Python telle quelle. Python est un langage de type dynamique, mais s'il semble difficile à comprendre, ajoutons des annotations de type avec des commentaires opportuns.

Regardons maintenant la définition de Haskell.

newtype State s a = State { runState :: s -> (a, s) }

instance Monad (State s) where
        return a = State $ \s -> (a, s)
        m >>= k  = State $ \s -> let
                (a, s') = runState m s
                in runState (k a) s'

En regardant la définition, State semble être une monade avec la fonction s-> (a, s) à l'intérieur. C'est l'État, n'est-ce pas? Dans la valeur de retour taple, a sur la gauche est le résultat du changement, et s sur la droite est l'état après le changement.

D'un point de vue linguistique procédural, celui-ci donne une impression étrange!

À propos, pour ceux qui ne connaissent pas la grammaire de Haskell, Haskell déclare les expressions lambda sous la forme \ argument-> expression. De plus, le mot-clé let est une expression let, et les variables sont liées sous let. ʻIn` C'est comme renvoyer l'expression suivante. Je le veux aussi pour Python (je l'ai fait http://qiita.com/hatchinee/items/1face0ffd5466de1ad97).

Et il existe une autre définition d'une fonction qui obtient et réécrit l'état. Ceci est déclaré comme une classe de type appelée MonadState.

class (Monad m) => MonadState s m | m -> s where
        get :: m s
        put :: s -> m ()

instance MonadState s (State s) where
        get   = State $ \s -> (s, s)
        put s = State $ \_ -> ((), s)

La puissance d'Haskell est faible, alors qu'est-ce qu'une classe de type ou une instance est une POO? Et diverses questions Il disparaît quand il flotte (* myuon_myon expliqué dans les commentaires. Merci!) Traduisez la partie fonction sans souci.

État de la mise en œuvre

Commençons par State pour le moment.


class State(object):
    def __init__(self, a):
        ''' private '''
        self.run_state = a

    @classmethod
    def ret(_, a):
        ''' return
        '''
        return State(lambda s: (a, s))

    def bind(self, func):
        ''' >>=
        '''
        def _(a):
            v, st = self.run_state(a)
            return func(v).run_state(st)
        return State(_)

    def __rshift__(self, func):
        ''' define >> as bind '''
        return self.bind(func)

runState est un membre self.run_state Puisque return est un mot réservé en Python, déclarez-le comme une méthode de classe appelée ret. >> = est appelé >> car il ressemble à une méthode de liaison ou quelque chose de similaire. Rendez-le disponible dans l'opérateur. Je pense que «>>» a été utilisé par un autre opérateur Monad, donc si vous pensez que c'est dangereux, vous pouvez en utiliser un autre.

Définition de MonadState

class MonadState(object):
    @classmethod
    def get(_):
        ''' get = State $ \s -> (s, s)
        '''
        return State(lambda s: (s, s))

    @classmethod
    def put(_, a):
        ''' put s = State $ \_ -> ((), s)
        '''
        return State(lambda s: (None, a))

État de la Monade. J'ai essayé de mettre la définition en Haskell dans le commentaire, mais elle peut être implémentée presque telle quelle. Utilisons maintenant ces implémentations pour voir si la réécriture et la récupération d'état fonctionnent.

>>> (MonadState.put(12) >> (lambda _: \
        MonadState.get())).run_state(13)
(12, 12)

>>> (MonadState.put(12) >> (lambda _: \
            MonadState.get() >> (lambda i: \
            MonadState.put(i * 2)))).run_state(13)
(None, 24)

Je pense que cela fonctionnera si vous l'exécutez comme une invite ou un doctest.

La monade d'État consiste probablement à faire abstraction de la procédure en synthétisant la fonction et en y donnant l'état. Je pensais que c'était un mécanisme qui pouvait avoir des résultats ou des effets secondaires sur la maladie.

Cela peut prendre un certain temps à comprendre car le comportement de get and put et la partie composition dans bind sont compliqués. Que se passe-t-il lorsque vous essayez de l'implémenter vous-même ou de créer quelque chose à l'aide de State Je pense que vous pourrez le sentir dans votre cœur.

Édition pratique

Maintenant que je l'ai ressenti dans mon cœur, faisons quelque chose de bien en utilisant la monade d'État. En parlant de piles, je vais faire une calculatrice polonaise inversée.

Pour le moment, définissez une fonction auxiliaire qui convertit une chaîne de caractères en opérateur.

from operator import add, sub, div, mul, concat

_OP_HOLDER = {
    '+': add,
    '-': sub,
    '*': mul,
    '/': div,
}


def _op(st):
    op = _OP_HOLDER[st]

    def _(x1, x2):
        '''Puisqu'il est difficile de contrôler l'ordre des pops, inversez ici'''
        print x2, st, x1
        return [op(x2, x1)]
    return _


def _concat(xs, ys):
    print xs, ys
    return concat(xs, ys)


def _is_valid_operator(v):
    return v in ['+', '-', '*', '/']

Ensuite, implémentez la partie push ou calcul sur la calculatrice.


class Calculator(object):
    @classmethod
    def push(_, val):
        if _is_valid_operator(val):
            return \
                MonadState.get() >> (lambda x:
                MonadState.put(
                    _concat(
                        x, _op(val)(x.pop(), x.pop()))))
        else:
            return \
                MonadState.get() >> (lambda x:
                MonadState.put(_concat(x, [val])))

    @classmethod
    def run(_, stk):
        _, ret = stk.run_state([])
        return ret.pop()

C'est gênant, donc la partie pop accède directement à la liste interne.

Testons-le.

if __name__ == '__main__':
    C = Calculator
    print '1 2 + 4 5 + *'
    calc = \
        C.push(1) >> (lambda _:
        C.push(2) >> (lambda _:
        C.push('+') >> (lambda _:
        C.push(4) >> (lambda _:
        C.push(5) >> (lambda _:
        C.push('+') >> (lambda _:
        C.push('*')))))))
    assert C.run(calc) == (1 + 2) * (4 + 5)
    '''
    1 2 + 4 5 + *
    [] [1]
    [1] [2]
    1 + 2
    [] [3]
    [3] [4]
    [3, 4] [5]
    4 + 5
    [3] [9]
    3 * 9
    [] [27]
Devrait être imprimé comme!
    '''

    print '\n5 4 3 - +'
    calc = \
        C.push(5) >> (lambda _:
        C.push(4)) >> (lambda _:
        C.push(3)) >> (lambda _:
        C.push('-')) >> (lambda _:
        C.push('+'))
    assert C.run(calc) == (5 + 4 - 3)

    print '\n6 2 + 5 3 - 2 * /'
    calc = \
        C.push(6) >> (lambda _:
        C.push(2) >> (lambda _:
        C.push('+') >> (lambda _:
        C.push(5) >> (lambda _:
        C.push(3) >> (lambda _:
        C.push('-') >> (lambda _:
        C.push(2) >> (lambda _:
        C.push('*') >> (lambda _:
        C.push('/')))))))))
    assert C.run(calc) == (6 + 2) / ((5 - 3) * 2)

Comment c'est? Je suis nouveau dans l'implémentation d'une calculatrice de notation polonaise inversée, donc je ne suis pas sûr. Je pense que ça marche bien.

La partie opération fonctionne avec ou sans fonctions d'ordre supérieur. Si vous utilisez des fonctions d'ordre supérieur, il semble que vous puissiez également effectuer des calculs (tels que la somme) en utilisant l'agrégation des résultats de chaque opération.

Cela ne semble pas être directement pratique pour écrire Python, mais je l'ai apprécié comme exercice cérébral. Merci pour votre soutien.


Si vous aimez cela, veuillez également le vérifier (Résumé des articles sur le codage de Python semblable à la magie noire que j'ai écrit jusqu'à présent -à propos-python)))

Recommended Posts

Monade d'État en Python
Modèle d'espace d'états personnalisé en Python
Python en optimisation
CURL en Python
Métaprogrammation avec Python
Python 3.3 avec Anaconda
Géocodage en python
Méta-analyse en Python
Unittest en Python
Époque en Python
Discord en Python
Allemand en Python
DCI en Python
tri rapide en python
nCr en python
N-Gram en Python
Programmation avec Python
Plink en Python
Constante en Python
FizzBuzz en Python
Sqlite en Python
Étape AIC en Python
LINE-Bot [0] en Python
CSV en Python
Assemblage inversé avec Python
Réflexion en Python
Constante en Python
nCr en Python.
format en python
Scons en Python 3
Puyopuyo en python
python dans virtualenv
PPAP en Python
Quad-tree en Python
Réflexion en Python
Chimie avec Python
Hashable en Python
DirectLiNGAM en Python
LiNGAM en Python
Aplatir en Python
Aplatir en python
Apprenez le modèle de conception "État" en Python
AtCoder # 36 quotidien avec Python
Texte de cluster en Python
Daily AtCoder # 32 en Python
Daily AtCoder # 6 en Python
Daily AtCoder # 18 en Python
Modifier les polices en Python
Motif singleton en Python
Opérations sur les fichiers en Python
Lire DXF avec python
Daily AtCoder # 53 en Python
Séquence de touches en Python
Utilisez config.ini avec Python
Résoudre ABC168D en Python
AtCoder # 7 tous les jours avec Python
Décomposition LU en Python
Une doublure en Python