paint-brush
Écrire un essai infiniment long à l'aide d'un modèle d'état en Pythonpar@aayn
2,430 lectures
2,430 lectures

Écrire un essai infiniment long à l'aide d'un modèle d'état en Python

par Aayush Naik10m2023/12/27
Read on Terminal Reader

Trop long; Pour lire

Le modèle de conception d’état est constitué de machines à états dans un code orienté objet. Nous utilisons le modèle pour créer un générateur d’essai/phrase.
featured image - Écrire un essai infiniment long à l'aide d'un modèle d'état en Python
Aayush Naik HackerNoon profile picture
0-item

Écrivons un essai infiniment long ! Mais c'est une tâche impossible. Cependant, nous pouvons créer un processus qui, s’il était exécuté indéfiniment, générerait un essai infiniment long. Assez proche .


Désormais, vous pouvez évidemment générer un essai long et répétitif avec une seule ligne de code Python :


 >>> "This is water. " * 20 'This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. '


Yaawn … huée ! Au lieu de cela, nous générerons un essai beaucoup plus intéressant en utilisant le modèle de conception d'État dans cet article.


Tout d’abord, nous comprendrons ce que sont les machines à états et comment elles sont liées au modèle de conception d’état. Ensuite, nous créerons une machine à états capable de générer un essai (raisonnablement) intéressant et infini. Ensuite, nous passerons brièvement en revue le modèle de conception de l’État. Enfin, nous traduirons cette machine à états en code orienté objet en utilisant le modèle de conception d'état.


Les modèles de conception logicielle sont des moyens efficaces pour résoudre les problèmes courants. Lorsqu'ils sont appliqués de manière appropriée, les modèles de conception logicielle tels que le modèle d'état peuvent vous aider à écrire des logiciels mieux évolutifs, maintenables et testables.

Machine à états

Essentiellement, le modèle de conception State traduit une machine à états en code orienté objet.


Si vous n'êtes pas familier avec les machines à états, c'est un concept assez simple. Une machine à états a des états et des transitions . Les états sont certaines propriétés de notre système d'intérêt, et les transitions d'état sont des actions qui modifient ces propriétés et provoquent ainsi également un changement d'état.


Étant donné que j'ai une formation en robotique (entre autres) et que les machines à états sont largement utilisées en robotique, je vais utiliser un exemple simple de robot aspirateur pour illustrer le fonctionnement des machines à états.


Machine à états pour piloter un robot aspirateur.


Le diagramme de la machine à états donne une image intuitive du fonctionnement du robot, même si vous n'avez jamais rencontré de machines à états. Reprenons cette opération étape par étape.


  1. Le robot démarre dans l'état Docked (le point noir indique l'état de démarrage).
  2. Si le robot détecte que sa batterie est faible, il commence à se charger (état de charge ) jusqu'à ce que sa batterie soit pleine. Une fois la batterie pleine, elle revient à l'état Docked .
  3. En état Docked , si le robot détecte que le sol est sale (et que sa batterie n'est pas faible), il commence à nettoyer le sol (état Nettoyage ).
  4. En état de nettoyage , si la batterie du robot est faible, il se charge automatiquement. Et si le sol est propre, le robot retourne à son dock (état Docked ).


Ainsi, notre robot aspirateur a trois états — Docked , Cleaning et Charging — et possède des transitions basées sur la détection sensorielle du sol et de sa batterie.


Machine à états simple pour un essai infini

Maintenant que nous comprenons les machines à états à un niveau de base, créons une machine à états capable d'écrire un essai infini.


Machine à états pour générer un essai infini - version 1


Ci-dessus se trouve une machine à états qui utilise la grammaire anglaise pour générer un essai composé de phrases courtes et simples. Je vous promets que nous arriverons très bientôt à une version plus intéressante, mais cela devrait constituer un bon point de départ pour comprendre. Voyons comment cela fonctionne.


  1. En commençant par l'état Nom , nous générons un nom en choisissant parmi une liste prédéfinie de noms. Disons que notre nom est « Le Monde ». (phrase jusqu'à présent : "Le Monde")
  2. Ensuite, nous nous retrouvons dans l’état Verbe , générant ensuite un verbe (disons « aboie »). (phrase jusqu'à présent : « Le monde aboie »)
  3. Nous générons un adjectif (disons « rouge ») dans l’état Adjectif . (phrase jusqu'à présent : « Le monde aboie rouge »)
  4. Ensuite, dans l'état Endmark , nous générons l'un des signes de ponctuation de fin, disons « ! ». (phrase : « Le monde aboie rouge ! »)
  5. Enfin, nous sommes de retour à l'état Nom pour générer notre prochaine phrase dans l'essai.


Cette machine à états pourrait générer un essai (absurde) qui ressemble à ceci.


Le monde aboie rouge ! Le cousin Harry pleut mal ? Les tigres scintillent de façon amusante. …


Machine à états non déterministe pour un essai infini

Bien que « non déterministe » semble compliqué, pour nos besoins, cela signifie simplement ajouter un peu de hasard. Essentiellement, nous ajoutons une sorte de tirage au sort avant de passer à certains États. Vous verrez ce que je veux dire.

Machine à états non déterministe pour générer un essai infini - version 2


La machine à états non déterministe ci-dessus est très similaire à la précédente. Les seules différences sont :

  • Les négations sont des mots comme « non » ou « non », et les conjonctions sont des mots comme « et » et « mais ».
  • Dans l’état Verbe , nous générons un verbe puis nous tirons à pile ou face. S'il tombe face (probabilité de 50 %), nous passons à l'état de négation ; sinon on passe à l'état Adjectif .
  • De même, dans l’état Adjectif , nous générons un adjectif puis tirons une pièce de monnaie. Si c’est face, nous passons à l’état Conjonction ; si c'est pile, alors nous passons à l'état Endmark .


Avec l’introduction du caractère aléatoire, de la négation et des conjonctions, nous pouvons désormais générer des phrases plus intéressantes et de longueur variable.


Modèle de conception d'état

Voyons maintenant comment fonctionne le modèle de conception d'état. Encore une fois, rappelez-vous que nous essayons de traduire une machine à états en code orienté objet.


Dans la machine à états de génération d’essais, observez que chaque état doit faire deux choses.

  1. Effectuez une action. Dans ce cas, générer un mot (nom, adjectif, etc.).
  2. Transition vers l'état suivant. Du nom au verbe , etc.


Du point de vue d’un État particulier, il n’y a rien d’autre qu’il ait besoin de savoir ou de faire. Au lieu de nous enliser dans la complexité du système dans son ensemble – tous ses états et transitions – nous pouvons nous concentrer uniquement sur un état à la fois. À mon avis, ce type d’ isolement et de découplage constitue le principal argument de vente du modèle étatique.


Ci-dessous, nous avons un diagramme UML pour le modèle de conception State. Il existe un certain contexte dans lequel chacun des états opère, illustré par la classe Context . L'objet contextuel possède un attribut d'état privé, qu'il utilise pour appeler l'état actuel afin d'effectuer son action. Chaque état implémente une interface State avec des méthodes pour effectuer son action ou opération et renvoyer l'état suivant.


Diagramme UML du modèle de conception d'état


Si nous mappons cela sur l'exemple de génération d'essais, le diagramme UML ressemble à ceci.


Modèle d'état implémenté pour la génération d'essais


WordState est désormais une classe abstraite (indiquée en italique) au lieu d'une interface. Les classes abstraites peuvent avoir des méthodes et des attributs abstraits (non implémentés), tandis que d'autres peuvent être définis. Les interfaces sont totalement abstraites : toutes leurs méthodes sont abstraites. J'ai effectué ce changement car l'implémentation generateWord est la même dans tous les États et il est bon d'éviter le code en double.


Décomposons chacun des attributs et méthodes ci-dessus. Dans la classe EssayContext , nous avons :

  • state : Référence à l’objet WordState actuel.
  • essayBody : Liste de tous les mots générés jusqu'à présent.
  • setState() : Setter pour changer l'attribut state .
  • addWord() : Méthode pour ajouter le mot suivant au corps de l'essai.
  • generateEssay() : Nous appelons cette méthode pour générer notre essai ; nous nous arrêtons lorsque l' essayBody a une longueur supérieure à length .
  • printEssay() : renvoie une chaîne de l'essai généré.


Dans la classe abstraite WordState , nous avons :

  • wordList : Propriété abstraite (indiquée en italique) pour une liste de mots à partir de laquelle nous choisissons les mots à générer.
  • generateWord() : Méthode qui ajoute le mot généré au contexte de l'essai.
  • nextState() : Méthode abstraite pour renvoyer l'état suivant.


Nous utiliserons NounState comme exemple représentatif pour tous les autres états concrets hérités de WordState .

  • wordList : Une liste de noms à partir de laquelle nous choisissons les mots à générer.
  • nextState() : Renvoie l'état suivant.


Maintenant, nous avons tout ce dont nous avons besoin pour implémenter cela dans le code. Allons-y et faisons exactement cela !


Code Python

Écrivons d'abord la classe EssayContext dans un fichier appelé essay_context.py . Nous allons abandonner le cas du chameau et passer au cas du serpent parce que, eh bien, Python est un... serpent (désolé).


 from word_state import WordState class EssayContext: def __init__(self, state: WordState): self.state = state self.essay_body: list[str] = [] def set_state(self, state: WordState): self.state = state def add_word(self, word: str): self.essay_body.append(word) def generate_essay(self, length: int): while len(self.essay_body) < length: self.state.generate_word(self) def print_essay(self) -> str: return " ".join(self.essay_body)


Ensuite, ajoutons les états dans un fichier appelé word_state.py .


 import abc import numpy as np class WordState(abc.ABC): word_list: list[str] @classmethod def generate_word(cls, context: "EssayContext"): word = np.random.choice(cls.word_list) context.add_word(word) context.set_state(cls.next_state()) @classmethod @abc.abstractmethod def next_state(cls) -> "WordState": pass class NounState(WordState): word_list: list[str] = ["everything", "nothing"] @classmethod def next_state(cls): return VerbState class VerbState(WordState): word_list: list[str] = ["is", "was", "will be"] @classmethod def next_state(cls): heads = np.random.rand() < 0.5 if heads: return NegationState return AdjectiveState class NegationState(WordState): word_list: list[str] = ["not"] @classmethod def next_state(cls): return AdjectiveState class AdjectiveState(WordState): word_list: list[str] = ["fantastic", "terrible"] @classmethod def next_state(cls): heads = np.random.rand() < 0.5 if heads: return ConjunctionState return EndmarkState class ConjunctionState(WordState): word_list: list[str] = ["and", "but"] @classmethod def next_state(cls): return NounState class EndmarkState(WordState): word_list: list[str] = [".", "!"] @classmethod def next_state(cls): return NounState


Enfin, ajoutons du code pour tout exécuter dans main.py .


 from essay_context import EssayContext from word_state import NounState if __name__ == '__main__': ctx = EssayContext(NounState) ctx.generate_essay(100) print(ctx.print_essay())


L'exécution python main.py nous donne le résultat suivant (différent à chaque fois en raison du non-déterminisme) :


 'everything is not terrible and nothing was terrible ! everything will be not fantastic but everything is fantastic . everything will be fantastic . nothing will be fantastic and nothing will be terrible ! everything was not fantastic and everything will be not terrible . everything was terrible . nothing was terrible but nothing will be fantastic ! nothing is not terrible . nothing was not fantastic but everything was not fantastic ! everything will be not fantastic but everything will be terrible ! everything will be not fantastic . everything is fantastic but nothing will be not terrible ! everything will be not fantastic but nothing was not fantastic !'


Pas mal pour un système aussi simple ! Nous pouvons également étendre les différentes listes de mots ou ajouter plus d'états pour rendre la génération d'essais plus sophistiquée. Nous pourrions même introduire des API LLM pour faire passer nos essais au niveau supérieur.


Dernières pensées

Les machines à états et le modèle State conviennent parfaitement à la modélisation et à la création de systèmes avec une notion bien définie d'« état ». Autrement dit, des comportements et des propriétés spécifiques sont associés à chaque état. Le robot aspirateur est en train de nettoyer, de se connecter ou de se charger. Votre téléviseur peut être allumé ou éteint, et les boutons de la télécommande du téléviseur agiront différemment en fonction de l'état du téléviseur.


C'est également un bon choix pour générer ou identifier des séquences avec un modèle bien défini. Cela s'applique à notre exemple de génération d'essais.


Enfin, vous pourriez demander : « À quoi ça sert tout cela ? » Pourquoi avons-nous pris tant de peine à définir les différents états et classes pour générer cet essai « infini » ? Nous aurions pu écrire 20 lignes (ou moins) de code Python pour obtenir le même comportement.


La réponse courte est pour une meilleure évolutivité .


Imaginez si, au lieu de trois ou cinq États, nous avions 50 ou 500 États. Ce n’est pas une hyperbole ; les vrais systèmes d’entreprise ont ce niveau de complexité. Le modèle étatique semble soudainement beaucoup plus attrayant en raison de son découplage et de son isolement. Nous pouvons nous concentrer uniquement sur un État à la fois sans avoir à garder l’ensemble du système en tête. Il est plus facile d’introduire des changements puisqu’un état n’affectera pas les autres. Il permet également de faciliter les tests unitaires, élément important d'un système évolutif et maintenable.


En fin de compte, le modèle étatique ne consiste pas seulement à gérer les États ; comme tous les modèles de conception, il s’agit d’un modèle pour construire des systèmes aussi évolutifs et maintenables que sophistiqués.