無限に長いエッセイを書いてみましょう!しかし、それは不可能な仕事です。ただし、永久に実行すると無限に長いエッセイが生成されるプロセスを作成することはできます。十分近い。
これで、明らかに 1 行の 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. '
やあ…ブーブー!代わりに、この記事では State デザイン パターンを使用して、より興味深いエッセイを生成します。
まず、ステート マシンとは何か、またステート マシンがステート デザイン パターンにどのように関連しているかを理解します。次に、(それなりに) 興味深い無限のエッセイを生成できるステート マシンを作成します。次に、状態の設計パターンについて簡単に説明します。最後に、状態設計パターンを使用して、その状態マシンをオブジェクト指向コードに変換します。
ソフトウェア設計パターンは、一般的に発生する問題を解決する効果的な方法です。状態パターンなどのソフトウェア設計パターンを適切に適用すると、スケーラブルで保守性が高く、テストしやすいソフトウェアを作成するのに役立ちます。
本質的に、ステート設計パターンはステート マシンをオブジェクト指向コードに変換します。
ステート マシンに詳しくない場合でも、これは非常に単純な概念です。ステート マシンには状態と遷移があります。状態は対象となるシステムの特定のプロパティであり、状態遷移はこれらのプロパティを変更し、それによって状態変化も引き起こすアクションです。
私にはロボット工学のバックグラウンドがあり、ロボット工学ではステート マシンが広く使用されているため、ロボット掃除機の簡単な例を使用してステート マシンがどのように動作するかを説明します。
ステート マシンに遭遇したことがない場合でも、ステート マシンの図により、ロボットがどのように動作するかが直感的にわかります。この操作を段階的に見てみましょう。
したがって、私たちのロボット掃除機には 3 つの状態 (ドッキング、掃除中、充電中) があり、床とバッテリーの感覚検出に基づいて遷移します。
ステート マシンを基本レベルで理解したので、無限のエッセイを作成できるステート マシンを作成しましょう。
上記は、英語の文法を使用して、短くて単純な文で構成されるエッセイを生成するステート マシンです。すぐにもっと興味深いバージョンに到達することを約束しますが、これは理解のための良い出発点として役立つはずです。仕組みを見てみましょう。
このステート マシンは、次のような (無意味な) エッセイを生成する可能性があります。
世界は赤く吠えます!いとこのハリーの雨はファウル?虎が楽しくきらめきます。 …
「非決定的」というと複雑に聞こえますが、私たちの目的では、ランダム性を追加することを意味します。基本的に、いくつかの状態に移行する前に、一種のコイントスを追加しています。私の言いたいことは分かるでしょう。
上記の非決定性ステート マシンは、前のステート マシンと非常によく似ています。唯一の違いは次のとおりです。
ランダム性、否定、接続詞の導入により、より興味深い可変長の文を生成できるようになりました。
ここで、状態設計パターンがどのように機能するかを理解しましょう。繰り返しになりますが、ステート マシンをオブジェクト指向コードに変換しようとしているということを思い出してください。
エッセイ生成ステート マシンでは、すべてのステートが 2 つのことを実行する必要があることに注目してください。
特定の州の観点から見ると、それ以外に知っておく必要があることや行う必要があることは何もありません。システム全体 (そのすべての状態と遷移) の複雑さに行き詰まってしまう代わりに、一度に 1 つの状態だけに集中することができます。私の考えでは、この種の分離と切り離しが、State パターンの最大のセールスポイントです。
以下に、State 設計パターンのUML図を示します。 Context
クラスで示されるように、各状態が動作する何らかのコンテキストがあります。コンテキスト オブジェクトにはプライベート状態属性があり、現在の状態を呼び出してアクションを実行するために使用されます。各状態は、そのアクションまたは操作を実行し、次の状態を返すためのメソッドを備えたState
インターフェイスを実装します。
これをエッセイ生成の例にマッピングすると、UML 図は次のようになります。
WordState
インターフェイスではなく抽象クラス (斜体で示されています) になりました。抽象クラスには、いくつかの抽象 (実装されていない) メソッドと属性を含めることができますが、その他のメソッドと属性を定義することもできます。インターフェイスは完全に抽象的です。インターフェイスのメソッドはすべて抽象的です。この変更を加えたのは、 generateWord
実装がすべての州で同じであり、コードの重複を避けることが望ましいためです。
上記の各属性とメソッドを詳しく見てみましょう。 EssayContext
クラスには次のものがあります。
state
: 現在のWordState
オブジェクトへの参照。essayBody
: これまでに生成されたすべての単語のリスト。setState()
: state
属性を変更するためのセッター。addWord()
: エッセイ本文に次の単語を追加するメソッド。generateEssay()
: このメソッドを呼び出してエッセイを生成します。 essayBody
の長さがlength
よりも大きい場合に停止します。printEssay()
: 生成されたエッセイの文字列を返します。
抽象クラスWordState
には次のものがあります。
wordList
: 生成する単語を選択する単語のリストの抽象プロパティ (斜体で示されています)。generateWord()
: 生成された単語をエッセイコンテキストに追加するメソッド。nextState()
: 次の状態を返すための抽象メソッド。
WordState
から継承される他のすべての具体的な状態の代表的な例としてNounState
を使用します。
wordList
: 生成する単語を選択する名詞のリスト。nextState()
: 次の状態を返します。
これで、これを実際にコードで実装するために必要なものがすべて揃いました。さあ、それではやってみましょう!
まず、 essay_context.py
というファイルにEssayContext
クラスを記述します。キャメルケースをやめてスネークケースに切り替えることにします。なぜなら、Python は...ヘビだからです (申し訳ありません)。
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)
次に、 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
最後に、 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())
python main.py
を実行すると、次の出力が得られます (非決定性のため毎回異なります)。
'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 !'
こんなシンプルなシステムも悪くないですね!また、さまざまな単語リストを拡張したり、状態を追加して、エッセイの生成をより洗練することもできます。エッセイを次のレベルに引き上げるために、いくつかの LLM API を導入することもできます。
ステート マシンとステート パターンは、明確に定義された「状態」の概念を使用してシステムをモデル化および作成するのに最適です。つまり、各状態に関連付けられた特定の動作とプロパティが存在します。ロボット掃除機は掃除中、ドッキング中、または充電中です。テレビはオンまたはオフにすることができ、テレビのリモコン ボタンはテレビの状態に応じて異なる動作をします。
また、明確に定義されたパターンを持つシーケンスの生成または識別にも適しています。これは、エッセイ生成の例に当てはまります。
最後に、「これには一体何の意味があるの?」と疑問に思うかもしれません。この「無限」のエッセイを生成するために、さまざまな状態やクラスを定義するのに苦労したのはなぜでしょうか?同じ動作を実現するには、20 行 (またはそれ以下) の Python コードを作成することもできます。
簡単に言うと、スケーラビリティを向上させるためです。
たった 3 つまたは 5 つの州ではなく、50 または 500 の州があった場合を想像してください。これは誇張ではありません。実際のエンタープライズ システムには、そのレベルの複雑性があります。突然、State パターンは、その切り離しと分離により、はるかに魅力的に見えます。システム全体を頭の中に留めておく必要がなく、一度に 1 つの状態にだけ集中できます。ある状態が他の状態に影響を与えないため、変更を導入するのが簡単になります。また、スケーラブルで保守可能なシステムの重要な部分である単体テストも容易になります。
結局のところ、State パターンは単に状態を管理するだけではありません。すべてのデザイン パターンと同様に、これは洗練されていると同時に拡張性と保守性も備えたシステムを構築するための青写真です。