エッセイを書いてみましょう!しかし、それは不可能な仕事です。ただし、永久に実行すると無限に長いエッセイが生成されるプロセスを作成することはできます。 。 無限に長い 十分近い これで、明らかに 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 つの状態 ( 、 、 ) があり、床とバッテリーの感覚検出に基づいて遷移します。 ドッキング 掃除中 充電中 無限エッセイのためのシンプルなステートマシン ステート マシンを基本レベルで理解したので、無限のエッセイを作成できるステート マシンを作成しましょう。 上記は、英語の文法を使用して、短くて単純な文で構成されるエッセイを生成するステート マシンです。すぐにもっと興味深いバージョンに到達することを約束しますが、これは理解のための良い出発点として役立つはずです。仕組みを見てみましょう。 状態から開始して、事前に定義された名詞のリストから選択して名詞を生成します。名詞が「The World」だとしましょう。 (ここまでの文章:「世界」) Noun そして、最終的に 状態になり、次に動詞 (たとえば、「吠える」) を生成します。 (ここまでの文章:「世界は吠える」) Verb 状態で形容詞 (たとえば、「赤」) を生成します。 (ここまでの文章:「世界は赤く吠える」) Adjective 次に、 ステートで、終了句読点の 1 つ (たとえば「!」) を生成します。 (文:「世界は赤く吠える!」) Endmark 最後に、 状態に戻り、エッセイ内の次の文を生成します。 名詞 このステート マシンは、次のような (無意味な) エッセイを生成する可能性があります。 世界は赤く吠えます!いとこのハリーの雨はファウル?虎が楽しくきらめきます。 … 無限エッセイの非決定性ステートマシン 「非決定的」というと複雑に聞こえますが、私たちの目的では、ランダム性を追加することを意味します。基本的に、いくつかの状態に移行する前に、一種のコイントスを追加しています。私の言いたいことは分かるでしょう。 上記の非決定性ステート マシンは、前のステート マシンと非常によく似ています。唯一の違いは次のとおりです。 否定は「いいえ」または「違う」のような単語であり、接続詞は「そして」や「しかし」のような単語です。 ステートでは、動詞を生成してからコインを投げます。表が出た場合 (50% の確率)、 状態に移行します。それ以外の場合は、 状態に進みます。 Verb 否定 形容詞 同様に、 状態では、形容詞を生成してからコインを投げます。表の場合は 状態に進みます。尾の場合は、 状態に進みます。 「形容詞」 コンジャンクション エンドマーク ランダム性、否定、接続詞の導入により、より興味深い可変長の文を生成できるようになりました。 状態設計パターン ここで、状態設計パターンがどのように機能するかを理解しましょう。繰り返しになりますが、ステート マシンをオブジェクト指向コードに変換しようとしているということを思い出してください。 エッセイ生成ステート マシンでは、すべてのステートが 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() これで、これを実際にコードで実装するために必要なものがすべて揃いました。さあ、それではやってみましょう! Pythonコード まず、 というファイルに クラスを記述します。キャメルケースをやめてスネークケースに切り替えることにします。なぜなら、Python は...ヘビだからです (申し訳ありません)。 essay_context.py EssayContext 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 パターンは単に状態を管理するだけではありません。すべてのデザイン パターンと同様に、これは洗練されていると同時に拡張性と保守性も備えたシステムを構築するための青写真です。