Lors de la programmation, je souhaite souvent "partager l'état avec plusieurs fonctions".
Pour le moment, il est préférable de choisir le meilleur parmi différentes solutions que d'utiliser des variables globales ou un seau relayer le pointeur sans réfléchir.
Par conséquent, je vais résumer la méthode de partage de l'état avec de multiples fonctions, ainsi que les avantages et les inconvénients, autant que je puisse penser.
Utilisez Python comme exemple de code et utilisez Racket (un type de Scheme) lorsque vous ne pouvez pas écrire en Python. parce que j'aime.
state = "initial state"
def fuga0():
#Changer après avoir fait référence à l'état
global state
print(f"fuga0: {state}")
state = "another state"
return "result of fuga0"
def fuga1():
#Se référer à l'état
print(f"fuga1: {state}")
def hoge():
print(fuga0())
fuga1()
print(f"last: {state}")
hoge()
Résultat (ce qui suit est omis)
fuga0: initial state
result of fuga0
fuga1: another state
last: another state
Pas un style d'écriture très recommandé.
from dataclasses import dataclass
from typing import Any
#Un pointeur auquel ce mec est relayé
@dataclass
class Box:
value: Any
def fuga0(box):
print(f"fuga0: {box.value}")
box.value = "another state"
return "result of fuga0"
def fuga1(box):
print(f"fuga1: {box.value}")
def hoge(box):
print(fuga0(box))
fuga1(box)
print(f"last: {box.value}")
b = Box("initial state")
hoge(b)
Dans le cas d'une constante, la valeur doit être relayée par compartiment au lieu du pointeur.
C'est un style d'écriture dit fonctionnel, et l'état ne change pas dans le code.
def fuga0(state):
print(f"fuga0: {state}")
return "result of fuga0", "another state"
def fuga1(state):
print(f"fuga1: {state}")
def hoge(state):
result, new_state = fuga0(state)
print(result)
fuga1(new_state)
print(f"last: {new_state}")
hoge("initial")
Une méthode souvent utilisée dans les langages orientés objet. Définissez l'état dans lequel vous souhaitez partager une variable membre.
class Hoge:
def __init__(self):
self._state = "initial state"
def _fuga0(self):
print(f"fuga0: {self._state}")
self._state = "another state"
return "result of fuga0"
def _fuga1(self):
print(f"fuga0: {self._state}")
def __call__(self):
print(self._fuga0())
self._fuga1()
print(f"last: {self._state}")
hoge = Hoge()
hoge()
Proche de l'orientation objet.
def create_hoge():
state = "initial"
def fuga0():
nonlocal state
print(f"fuga0: {state}")
state = "another state"
return "result of fuga0"
def fuga1():
print(f"fuga1: {state}")
def hoge():
print(fuga0())
fuga1()
print(f"last: {state}")
return hoge
create_hoge()()
Glocal Variable
Motif de mes pensées. Créez une variable qui ne peut être utilisée que dans avec. Voir le lien pour plus de détails.
param.py
_param = None
_initialized = False
@contextmanager
def parametrize(data):
global _param
global _initialized
before = _param
before_initialized = _initialized
_param = data
_initialized = True
try:
yield
finally:
_param = before
_initialized = before_initialized
def set_param(data):
if _initialized:
global _param
_param = data
else:
raise RuntimeError
def get_param():
if _initialized:
return _param
else:
raise RuntimeError
from param import get_param, set_param, parametrize
def fuga0():
print(f"fuga0: {get_param()}")
set_param("another state")
return "result of fuga0"
def fuga1():
print(f"fuga0: {get_param()}")
def hoge():
print(fuga0())
fuga1()
print(f"last: {get_param()}")
with parametrize("initial state"):
hoge()
Qu'est-ce que Monad? Qu'est-ce que State Monad? S'il vous plaît google pour quelque chose comme ça.
Écrivons avec Racket. Je ne suis pas sûr de l'implémentation de l'applicatif
;Définition de la monade d'État à partir d'ici
(require (prefix-in base: racket/base))
(require data/functor)
(require data/applicative)
(require data/monad)
(struct result (value state) #:transparent)
(struct state (f)
#:methods gen:functor
[(define (map g x)
(state (λ (s)
(match-define (result v ss) (run x s))
(result (g v) ss))))]
#:methods gen:applicative
[(define (pure _ x)
(state (λ (s) (result x s))))
(define (apply f xs)
(define (get-args xs s)
(match xs
[(cons x rest)
(match-define (result xv xs) (run x s))
(match-define (result args argss) (get-args rest xs))
(result (cons xv args) argss)]
[_ (result `() s)]))
(state (λ (s)
(match-define (result fv fs) (run f s))
(match-define (result args argss) (get-args xs fs))
(result (base:apply fv args) argss))))]
#:methods gen:monad
[(define (chain f x)
(state (λ (s)
(match-define (result xv xs) (run x s))
(match-define (result fv fs) (run (f xv) xs))
(result fv fs))))])
(define (run m s) ((state-f m) s))
(define get
(state (λ (s) (result s s))))
(define (set ns)
(state (λ (s) (result s ns))))
;Définition jusqu'à ici
(define fuga0
(do [x <- get]
(pure (printf "fuga0: ~a\n" x))
(set "another state")
(pure "result of fuga0")))
(define fuga1
(do [x <- get]
(pure (printf "fuga1: ~a\n" x))))
(define hoge
(do [x <- fuga0]
(pure (displayln x))
fuga1
[y <- get]
(pure (printf "last: ~a\n" y))))
(run hoge "initial state")
Pour les constantes, utilisez la monade Reader.
C'est assez pénible en Python, mais si vous faites de votre mieux, cela ressemble à ceci.
#Définition de la monade d'État à partir d'ici
from typing import Callable, TypeVar, Generic, Tuple
S = TypeVar("S") #Type d'état
R = TypeVar("R") #Type de retour
A = TypeVar("A") #Nouveau type de retour
class State(Generic[S, R]):
def __init__(self, f: Callable[[S], Tuple[S, R]]):
self._f = f
def run(self, state: S) -> Tuple[S, R]:
return self._f(state)
@staticmethod
def of(value: R):
return State(lambda s: (value, s))
def flatmap(self, g: Callable[[R], State[S, A]]) -> State[S, A]:
def _new(state):
f_ret, f_state = self.run(state)
return g(f_ret).run(f_state)
return State(_new)
def map(self, g: Callable[[R], A]) -> State[S, A]:
return self.flatmap(lambda x: State.of(g(x)))
def then(self, m: State[S, A]) -> State[S, A]:
return self.flatmap(lambda _: m)
def value(self, v: A) -> State[S, A]:
return self.map(lambda _: v)
get_m = State(lambda s: (s, s))
def set_m(new_state):
return State(lambda s: (s, new_state))
#Définition de la monade d'État jusqu'à présent
fuga0 = (get_m
.map(lambda v: print(f"fuga0: {v}"))
.then(set_m("another_state"))
.value("result of fuga0"))
fuga1 = (get_m
.map(lambda v: print(f"fuga1: {v}")))
hoge = (fuga0
.map(print)
.then(fuga1)
.then(get_m)
.map(lambda v: print(f"last: {v}")))
hoge.run("initial state")
Qu'est-ce que la continuation limitée? Alors, pourquoi pouvez-vous gérer les changements d'état? Pour ceux qui sont intéressés, voir le tutoriel d'Asai-sensei.
(require racket/control)
(define (get)
(shift k
(λ (x)
((k x) x))))
(define (set s)
(shift k
(λ (x)
((k x) s))))
(define (run m s)
((reset
(let ([ret (m)])
(λ (_) ret))) s))
(define (fuga0)
(printf "fuga0: ~a\n" (get))
(set "another state")
"result of fuga0")
(define (fuga1)
(printf "fuga1: ~a\n" (get)))
(define (hoge)
(displayln (fuga0))
(fuga1)
(printf "last: ~a\n" (get)))
(run hoge "initial state")
Tout le monde dit tout le monde différemment.
Au lieu de vous en tenir à ce que vous savez en tant que programmeur, augmentez le nombre d'outils et utilisez-les correctement.
De plus, il y a contextvars dans la bibliothèque Python standard. Je ne l'ai pas mentionné cette fois car il semble avoir été conçu dans un esprit asynchrone.
Recommended Posts