paint-brush
Написание бесконечно длинного эссе с использованием шаблона состояния в Pythonк@aayn
2,430 чтения
2,430 чтения

Написание бесконечно длинного эссе с использованием шаблона состояния в Python

к Aayush Naik10m2023/12/27
Read on Terminal Reader

Слишком долго; Читать

Шаблон проектирования состояний — это конечные автоматы в объектно-ориентированном коде. Мы используем шаблон для создания генератора эссе/предложений.
featured image - Написание бесконечно длинного эссе с использованием шаблона состояния в Python
Aayush Naik HackerNoon profile picture
0-item

Давайте напишем бесконечно длинное сочинение! Но это невыполнимая задача. Однако мы можем создать процесс, который, если его запустить бесконечно, создаст бесконечно длинное эссе. Достаточно близко .


Теперь вы, очевидно, можете создать длинное и повторяющееся эссе с помощью одной строки кода 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 преобразует конечный автомат в объектно-ориентированный код.


Если вы не знакомы с конечными автоматами, это довольно простая концепция. Конечный автомат имеет состояния и переходы . Состояния — это определенные свойства интересующей нас системы, а переходы состояний — это действия, которые изменяют эти свойства и тем самым также вызывают изменение состояния.


Поскольку у меня есть опыт работы в робототехнике (помимо прочего) и поскольку в робототехнике широко используются конечные автоматы, я воспользуюсь простым примером робота-пылесоса, чтобы проиллюстрировать, как работают конечные автоматы.


Государственная машина для управления роботом-пылесосом.


Диаграмма конечного автомата рисует интуитивно понятную картину того, как работает робот, даже если вы никогда не сталкивались с конечными автоматами. Давайте разберем эту операцию шаг за шагом.


  1. Робот запускается в состоянии «Пристыкован» (черная точка указывает на стартовое состояние).
  2. Если робот обнаруживает, что его батарея разряжена, он начинает заряжаться самостоятельно (состояние зарядки ), пока его батарея не заполнится полностью. Как только аккумулятор заряжен, он возвращается в состояние «Пристыкован» .
  3. В состоянии «Пристыкован» , если робот обнаруживает, что пол грязный (и его батарея не разряжена), он начинает мыть пол (состояние «Уборка» ).
  4. В состоянии «Уборка» , если у робота разряжается батарея, он начинает заряжаться самостоятельно. И если пол чистый, робот возвращается в свою док-станцию (Состояние Docked ).


Таким образом, наш робот-пылесос имеет три состояния — «Пристыкован» , «Уборка » и «Зарядка » — и имеет переходы, основанные на сенсорном обнаружении пола и его аккумулятора.


Простой конечный автомат для бесконечного эссе

Теперь, когда мы понимаем конечные автоматы на базовом уровне, давайте создадим конечный автомат, способный написать бесконечное эссе.


Конечный автомат для генерации бесконечного эссе — версия 1


Выше представлен конечный автомат, который использует английскую грамматику для создания эссе, состоящего из коротких простых предложений. Обещаю, очень скоро мы доберемся до более интересной версии, но это должно послужить хорошей отправной точкой для понимания. Давайте рассмотрим, как это работает.


  1. Начиная с состояния «Существительное» , мы генерируем существительное, выбирая его из некоторого заранее определенного списка существительных. Допустим, наше существительное — «Мир». (предложение на данный момент: «Мир»)
  2. Затем мы оказываемся в состоянии «Глагол» , генерируя следующий глагол (скажем, «лает»). (предложение на данный момент: «Мир лает»)
  3. Мы генерируем прилагательное (скажем, «красный») в состоянии «Прилагательное» . (предложение на данный момент: «Мир лает красным»)
  4. Затем, в состоянии Endmark , мы генерируем один из завершающих знаков препинания, например «!». (предложение: «Мир лает красным!»)
  5. Наконец, мы вернулись в состояние существительного , чтобы сгенерировать следующее предложение в эссе.


Этот конечный автомат может сгенерировать (бессмысленное) эссе, которое выглядит следующим образом.


Мир лает красным! Кузен Гарри нарушил правила? Тигры весело мерцают. …


Недетерминированный конечный автомат для бесконечного эссе

Хотя «недетерминированный» звучит сложно, для наших целей это просто означает добавление некоторой случайности. По сути, мы добавляем своего рода подбрасывание монеты перед переходом в некоторые состояния. Вы поймете, что я имею в виду.

Недетерминированный конечный автомат для генерации бесконечного эссе — версия 2


Недетерминированный конечный автомат, описанный выше, очень похож на предыдущий. Единственные различия:

  • Отрицания — это такие слова, как «нет» или «не», а союзы — это такие слова, как «и» и «но».
  • В состоянии «Глагол» мы генерируем глагол, а затем подбрасываем монету. Если выпадает решка (вероятность 50%), мы переходим в состояние Отрицания ; в противном случае мы переходим в состояние Прилагательного .
  • Аналогично, в состоянии «Прилагательное» мы генерируем прилагательное, а затем подбрасываем монету. Если выпадает решка, мы переходим в состояние Конъюнкции ; если решка, то переходим в состояние Endmark .


Благодаря введению случайности, отрицания и союзов мы теперь можем генерировать более интересные предложения переменной длины.


Шаблон проектирования состояний

Теперь давайте разберемся, как работает шаблон проектирования состояний. Опять же, помните, что мы пытаемся преобразовать конечный автомат в объектно-ориентированный код.


Обратите внимание, что в машине состояний генерации эссе каждое состояние должно сделать две вещи.

  1. Выполните какое-либо действие. В данном случае образуется слово (существительное, прилагательное и т. д.).
  2. Переход в следующее состояние. От существительного к глаголу и так далее.


С точки зрения конкретного государства, ему больше ничего не нужно знать или делать. Вместо того, чтобы увязнуть в сложности всей системы — всех ее состояний и переходов — мы можем просто сосредоточиться на одном состоянии за раз. На мой взгляд, такого рода изоляция и разъединение является самым большим преимуществом модели государства.


Ниже у нас есть UML- диаграмма для шаблона проектирования «Состояние». Существует некоторый контекст, в котором действует каждое из состояний, иллюстрируемый классом Context . Объект контекста имеет атрибут частного состояния, который он использует для вызова текущего состояния для выполнения своего действия. Каждое состояние реализует интерфейс State с методами для выполнения его действия или операции и возврата следующего состояния.


UML-диаграмма шаблона проектирования состояний


Если мы сопоставим это с примером создания эссе, диаграмма UML будет выглядеть следующим образом.


Шаблон состояния реализован для генерации эссе


WordState теперь является абстрактным классом (обозначенным курсивом), а не интерфейсом. Абстрактные классы могут иметь некоторые абстрактные (не реализованные) методы и атрибуты, а другие могут быть определены. Интерфейсы полностью абстрактны: все их методы абстрактны. Я внес это изменение, потому что generateWord одинакова во всех состояниях, и избегать дублирования кода полезно.


Давайте разберем каждый из атрибутов и методов, описанных выше. В классе EssayContext у нас есть:

  • state : ссылка на текущий объект WordState .
  • essayBody : Список всех слов, сгенерированных на данный момент.
  • setState() : установщик для изменения атрибута state .
  • addWord() : метод добавления следующего слова в текст эссе.
  • generateEssay() : мы вызываем этот метод для создания нашего эссе; мы останавливаемся, когда длина essayBody превышает length .
  • printEssay() : возвращает строку сгенерированного эссе.


В абстрактном классе WordState у нас есть:

  • wordList : Абстрактное свойство (обозначено курсивом) для списка слов, из которого мы выбираем слова для генерации.
  • generateWord() : метод, который добавляет сгенерированное слово в контекст эссе.
  • nextState() : абстрактный метод для возврата следующего состояния.


Мы будем использовать NounState в качестве репрезентативного примера для всех других конкретных состояний, унаследованных от WordState .

  • wordList : список существительных, из которого мы выбираем слова для создания.
  • nextState() : возвращает следующее состояние.


Теперь у нас есть все необходимое, чтобы реализовать это в коде. Давайте продолжим и сделаем именно это!


Код Python

Давайте сначала напишем класс EssayContext в файле с именем essay_context.py . Мы откажемся от верблюжьего случая и перейдем к случаю со змеей, потому что Питон — это... змея (извините).


 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 !'


Неплохо для такой простой системы! Мы также можем расширить различные списки слов или добавить больше состояний, чтобы сделать создание эссе более сложным. Мы могли бы даже представить некоторые API-интерфейсы LLM, чтобы вывести наши эссе на новый уровень.


Последние мысли

Конечные автоматы и шаблон State отлично подходят для моделирования и создания систем с четко определенным понятием «состояние». То есть с каждым состоянием связано определенное поведение и свойства. Робот-пылесос выполняет уборку, док-станцию или зарядку. Ваш телевизор может быть включен или выключен, а кнопки пульта дистанционного управления телевизора будут действовать по-разному в зависимости от состояния телевизора.


Он также хорошо подходит для создания или идентификации последовательностей с четко определенным шаблоном. Это относится к нашему примеру создания эссе.


Наконец, вы можете спросить: «Какой во всем этом смысл?» Почему мы потратили столько времени на определение различных состояний и классов, чтобы создать это «бесконечное» эссе? Мы могли бы написать 20 (или меньше) строк кода Python, чтобы добиться такого же поведения.


Короткий ответ: для лучшей масштабируемости .


Представьте себе, если бы вместо трех или пяти штатов у нас было бы 50 или 500 штатов. Это не гипербола; реальные корпоративные системы имеют такой уровень сложности. Внезапно модель «Государство» кажется гораздо более привлекательной из-за ее разъединенности и изоляции. Мы можем просто сосредоточиться на одном состоянии за раз, не держа в голове всю систему. Вносить изменения проще, поскольку одно состояние не влияет на другие. Это также упрощает модульное тестирование, что является важной частью масштабируемой и поддерживаемой системы.


В конечном счете, шаблон «Состояние» касается не только управления состояниями; Как и все шаблоны проектирования, это образец построения систем, которые являются настолько же масштабируемыми и удобными в обслуживании, насколько и сложными.