Staatsmonade in Python

Ich möchte Python verwenden. Und wenn es um Monaden geht, ist Vielleicht berühmt.

Vielleicht ist es relativ einfach zu verstehen und es ist einfach, es in verschiedenen Sprachen in die Praxis umzusetzen, so dass einige Leute es selbst implementieren. Ich denke es wird passieren. Aber, Ich denke, es ist besser, von der Staatsmonade zu verstehen Ich habe so einen Artikel gefunden.

Also werde ich die Staatsmonade implementieren. Versuchen Sie, einen Taschenrechner für Addition, Subtraktion, Multiplikation und Division mithilfe eines Stapels mit Haskell zu erstellen Haskell's State Monad (1) -Imitate State 6. Staatsmonade für die Verwendung des lokalen "Staates" Ich habe so etwas gelesen und mein Bewusstsein geschärft.

Ich kann das Gefühl nicht leugnen, dass es so viele Artikel gibt. Außerdem die Erklärung einschließlich des Referenzartikels Es ist konkret und leicht zu verstehen. Aus diesem Grund jedoch im Implementierungsteil des Ikansen-Stacks Ich habe mir zu viel Mühe gegeben, und ich hatte das Gefühl, dass es schwierig ist, das Wesen des Staates zu erfassen.

Verwenden Sie also eine etwas andere Sprache und implementieren Sie sie selbst als abstrakteren Mechanismus neu Lassen Sie uns versuchen, die Staatsmonade zu erfassen.

Definition in Haskell

Daher werde ich hier auf die Haskell-Implementierung verweisen, ohne über etwas Besonderes nachzudenken. Vorerst möchte ich das Wesentliche der Staatsmonade lernen, indem ich sie so wie sie ist auf Python portiert. Python ist eine dynamische Typensprache, aber wenn es schwer zu verstehen scheint, fügen wir Typanmerkungen mit zeitnahen Kommentaren hinzu.

Schauen wir uns nun die Definition von Haskell an.

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'

In der Definition scheint State eine Monade mit der Funktion "s-> (a, s)" zu sein. s ist Staat, nicht wahr? Im Rückgabewert Taple ist a links das Ergebnis der Änderung und s rechts der Zustand nach der Änderung.

Aus prozeduraler sprachlicher Sicht macht dieser einen seltsamen Eindruck!

Übrigens für diejenigen, die mit Haskells Grammatik nicht vertraut sind, Haskell deklariert Lambda-Ausdrücke in der Form "\ argument-> expression". Außerdem ist das Schlüsselwort let ein let-Ausdruck, und die Variablen sind unter let gebunden. in Es ist, als würde man den folgenden Ausdruck zurückgeben. Ich möchte es auch für Python (ich habe es http://qiita.com/hatchinee/items/1face0ffd5466de1ad97 gemacht).

Und es gibt eine andere Definition einer Funktion, die den Status abruft und neu schreibt. Dies wird als Typklasse mit dem Namen MonadState deklariert.

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)

Die Haskell-Leistung ist niedrig. Was ist also eine Typklasse oder eine Instanz ist eine OOP? Und verschiedene Fragen Es verschwindet, wenn es schwebt (* myuon_myon in den Kommentaren erklärt. Danke!) Übersetzen Sie den Funktionsteil ohne Verstand.

Staatliche Umsetzung

Beginnen wir vorerst mit State.


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 ist ein self.run_state Mitglied Da return in Python ein reserviertes Wort ist, deklarieren Sie es als Klassenmethode mit dem Namen ret. >> = heißt >>, weil es wie eine Bindemethode oder ähnliches aussieht. Stellen Sie es im Bediener zur Verfügung. Ich habe das Gefühl, dass >> von einem anderen Monad-Operator verwendet wurde, daher denke ich, dass es in Ordnung ist, einen anderen zu verwenden, wenn Sie der Meinung sind, dass dies gefährlich ist.

Definition von 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))

Monadenstaat. Ich habe versucht, die Definition in Haskell in den Kommentar aufzunehmen, aber sie kann fast so implementiert werden, wie sie ist. Lassen Sie uns nun diese Implementierungen verwenden, um zu sehen, ob das Umschreiben und Abrufen von Status funktioniert.

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

Ich denke, es wird funktionieren, wenn Sie es als Eingabeaufforderung oder als Test ausführen.

Die Staatsmonade besteht wahrscheinlich darin, das Verfahren zu abstrahieren, indem die Funktion synthetisiert und der Staat dort angegeben wird. Ich dachte, es wäre ein Mechanismus, der Ergebnisse oder Nebenwirkungen auf die Krankheit bringen könnte.

Das Verständnis kann einige Zeit dauern, da das Verhalten von get and put und der Kompositionsteil beim Binden kompliziert sind. Was passiert, wenn Sie versuchen, es selbst zu implementieren oder etwas mit State zu erstellen? Ich denke, du wirst es in deinem Herzen fühlen können.

Praktische Ausgabe

Jetzt, da Sie es irgendwie in Ihrem Herzen gespürt haben, lassen Sie uns mit der Staatsmonade etwas Schönes machen. Apropos Stapel, ich werde einen polnischen Umkehrrechner machen.

Definieren Sie vorerst eine Hilfsfunktion, die eine Zeichenfolge in einen Operator umwandelt.

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

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


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

    def _(x1, x2):
        '''Da es schwierig ist, die Pop-Reihenfolge zu steuern, invertieren Sie hier'''
        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 ['+', '-', '*', '/']

Implementieren Sie als Nächstes den Push- oder Berechnungsteil in den Rechner.


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

Es ist problematisch, so dass der Pop-Teil direkt auf die interne Liste zugreift.

Lass es uns testen.

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]
Sollte wie gedruckt werden!
    '''

    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)

Wie ist das? Ich bin neu in der Implementierung eines Rechners für die umgekehrte polnische Notation, daher bin ich mir nicht sicher. Ich denke, es funktioniert gut.

Der Operationsteil arbeitet mit oder ohne Funktionen höherer Ordnung. Wenn Sie Funktionen höherer Ordnung verwenden, können Sie anscheinend auch Berechnungen (z. B. Summe) durchführen, indem Sie die Ergebnisse jeder Operation aggregieren.

Es scheint nicht direkt praktisch zu sein, um Python zu schreiben, aber ich habe es als Gehirnübung genossen. Danke für Ihre Unterstützung.


Wenn Ihnen so etwas gefällt, lesen Sie es bitte auch durch ([Zusammenfassung der Artikel über die Kodierung von Python mit schwarzer Magie, die ich bisher geschrieben habe]) (http://hachibeechan.hateblo.jp/entry/summary-of-my-article) -über-Python)))

Recommended Posts

Staatsmonade in Python
Benutzerdefiniertes Zustandsraummodell in Python
Python in der Optimierung
CURL in Python
Metaprogrammierung mit Python
Python 3.3 mit Anaconda
Geokodierung in Python
Metaanalyse in Python
Unittest in Python
Epoche in Python
Zwietracht in Python
Deutsch in Python
DCI in Python
Quicksort in Python
nCr in Python
N-Gramm in Python
Programmieren mit Python
Plink in Python
Konstante in Python
FizzBuzz in Python
SQLite in Python
Schritt AIC in Python
LINE-Bot [0] in Python
CSV in Python
Reverse Assembler mit Python
Reflexion in Python
Konstante in Python
nCr in Python.
Format in Python
Scons in Python 3
Puyopuyo in Python
Python in Virtualenv
PPAP in Python
Quad-Tree in Python
Reflexion in Python
Chemie mit Python
Hashbar in Python
DirectLiNGAM in Python
LiNGAM in Python
In Python reduzieren
In Python flach drücken
Lernen Sie das Entwurfsmuster "State" in Python
Täglicher AtCoder # 36 mit Python
Clustertext in Python
Täglicher AtCoder # 32 in Python
Täglicher AtCoder # 6 in Python
Täglicher AtCoder # 18 in Python
Bearbeiten Sie Schriftarten in Python
Singleton-Muster in Python
Dateioperationen in Python
Lesen Sie DXF mit Python
Täglicher AtCoder # 53 in Python
Tastenanschlag in Python
Verwenden Sie config.ini mit Python
Löse ABC168D in Python
Täglicher AtCoder # 7 in Python
LU-Zerlegung in Python
Ein Liner in Python