Автоматное программирование

Поделись знанием:
Перейти к: навигация, поиск
Парадигмы программирования
 • Императивная
(контрастирует с декларативной)
Процедурная
Структурная
Аспектно-ориентированная
Объектно-ориентированная
Агентно-ориентированная
Компонентно-ориентированная
Прототипно-ориентированная
Обобщённое программирование

 • Декларативная
(контрастирует с императивной)

Чистота языка
Чистота функции
Функциональная
В терминах Рефал-машины
Аппликативная
Комбинаторная
Бесточечная
(чистая конкатенативная)
Логическая
Ограничениями

 • Конкатенативная
 • Векторная[en]
 • Метапрограммирование

Языково-ориентированная
Предметно-ориентированная
Пользователями[en]
Автоматизация процесса программирования
Рефлексивность
Гомоиконность[en]

 • Связанные темы

Программирование в крупном и мелком масштабе[en]
Модульность
Полиморфизм
Продолжения и CPS
Параллелизм и конкурентность

 • Методы и алгоритмы

Автоматное
Динамическое
Потоков данных
Событийно-ориентированное
Реактивное
Сервис-ориентированное

Автома́тное программи́рование — это парадигма программирования, при использовании которой программа или её фрагмент осмысливается как модель какого-либо формального автомата.

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

Определяющими для автоматного программирования являются следующие особенности:

  1. временной период выполнения программы разбивается на шаги автомата, каждый из которых представляет собой выполнение определённой (одной и той же для каждого шага) секции кода с единственной точкой входа; такая секция может быть оформлена, например, в виде отдельной функции и может быть разделена на подсекции, соответствующие отдельным состояниям или категориям состояний
  2. передача информации между шагами автомата осуществляется только через явно обозначенное множество переменных, называемых состоянием автомата; между шагами автомата программа (или её часть, оформленная в автоматном стиле) не может содержать неявных элементов состояния, таких как значения локальных переменных в стеке, адреса возврата из функций, значение текущего счётчика команд и т. п.; иначе говоря, состояние программы на любые два момента входа в шаг автомата могут различаться между собой только значениями переменных, составляющих состояние автомата (причём такие переменные должны быть явно обозначены в качестве таковых).

Полностью выполнение кода в автоматном стиле представляет собой цикл (возможно, неявный) шагов автомата.

Название автоматное программирование оправдывается ещё и тем, что стиль мышления (восприятия процесса исполнения) при программировании в этой технике практически точно воспроизводит стиль мышления при составлении формальных автоматов (таких как машина Тьюринга, автомат Маркова и др.)





Пример с использованием конечного автомата

Пусть, к примеру, требуется написать на языке Си программу, читающую из потока стандартного ввода текст, состоящий из строк, и для каждой строки печатающую первое слово этой строки и перевод строки. Ясно, что для этого во время чтения каждой строки следует сначала пропустить пробелы, если таковые есть в начале строки; затем читать буквы, составляющие слово, и печатать их, пока слово не кончится (то есть либо не кончится строка, либо не будет встречен пробельный символ); наконец, когда первое слово успешно считано и напечатано, необходимо дочитать строку до конца, ничего при этом не печатая. Встретив (на любой фазе) символ перевода строки, следует напечатать перевод строки и продолжить с начала. При возникновении (опять таки, на любой фазе) ситуации «конец файла» следует прекратить работу.

Императивная программа

Программа, решающая эту задачу в традиционном императивном стиле, может выглядеть, например, так (язык С):

#include <stdio.h>

int main() {
  char c;
  
  do {
    c = getchar();
    while (c == ' ') c = getchar();
    while (c != ' ' && c != '\n' && c != EOF) putchar(c), c = getchar();
    putchar('\n');
    while (c != '\n' && c != EOF) c = getchar();
  } while (c != EOF);
  
  return 0;
}

Программа в автоматном стиле

Ту же задачу можно решить, применив мышление в терминах конечных автоматов. Заметим, что разбор строки разделяется на три фазы: пропуск лидирующих пробелов, печать слова и пропуск символов остатка строки. Назовём эти три фазы состояниями before, inside и after. Программа теперь может выглядеть, например, так:

#include <stdio.h>

int main() {
  enum states { before, inside, after } state; 
  int c;
  
  state = before;
  while ((c = getchar()) != EOF) {
    switch (state) {
      
      case before:
        if (c == '\n') putchar('\n');
        else if (c != ' ') putchar(c), state = inside;
        break;
      
      case inside:
        switch (c) {
          case ' ':
            state = after; break;
          case '\n':
            putchar('\n'), state = before;
            break;
          default: putchar(c);
        }
        break;
        
      case after:
        if (c == '\n') putchar('\n'), state = before;
      
    }
  }

  return 0;
}

Несмотря на то, что код явно стал более длинным, у него имеется одно несомненное достоинство: чтение (то есть вызов функции getchar()) теперь выполняется ровно в одном месте. Кроме того, необходимо заметить, что вместо четырёх циклов, использовавшихся в предыдущей версии, цикл теперь используется только один. Тело цикла (за исключением действий, выполняемых в заголовке) представляет собой шаг автомата, сам же цикл задаёт цикл работы автомата.

Программа реализует (моделирует) работу конечного автомата, изображённого на рисунке. Буквой N на диаграмме обозначен символ конца строки, буквой S — символ пробела, буквой A — все остальные символы. За один шаг автомат делает ровно один переход в зависимости от текущего состояния и прочитанного символа. Некоторые переходы сопровождаются печатью прочитанного символа; такие переходы на диаграмме обозначены звёздочками.

Строго соблюдать разделение кода на обработчики отдельных состояний, вообще говоря, не обязательно. Более того, в некоторых случаях само понятие состояния может складываться из значений нескольких переменных, так что учесть все возможные их комбинации окажется практически невозможно. В рассматриваемом примере можно сэкономить объём кода, если заметить, что действия, выполняемые по символу «конец строки», от состояния не зависят. Программа, эквивалентная предыдущей, но написанная с учётом такого замечания, будет выглядеть так:

#include <stdio.h>

int main() {
  enum states { before, inside, after } state;
  int c;
  
  state = before;
  while ((c = getchar()) != EOF) {
    if (c == '\n') putchar('\n'), state = before, continue;
    switch (state) {
      case before:
        if (c != ' ') putchar(c), state = inside;
      break;
      case inside:
        if (c == ' ') state = after;
        else putchar(c);
      case after:
        break;
    }
  }

  return 0;
}

Выделение шага автомата в отдельную функцию

Основополагающим в вышеприведённой программе является по-прежнему чёткое выделение кода, отвечающего за шаг автомата. Это обстоятельство можно подчеркнуть ещё сильнее, если выделить шаг автомата в отдельную функцию.

#include <stdio.h>

enum States { before, inside, after };

void step(enum States &state, int &c) {
  if (c == '\n') putchar('\n'), state = before;
  switch (state) {
    case before:
      if (c != ' ') putchar(c), state = inside;
      break;
    case inside:
      if (c == ' ') state = after;
      else putchar(c);
    case after:
      break;
  }
}

int main() {
  int c; enum States state = before;

  while ((c = getchar()) != EOF) step(state, c);

  return 0;
}

Этот пример наглядно демонстрирует основное свойство, благодаря которому код можно считать оформленным в стиле автоматного программирования:

  1. отдельные шаги автомата выполняются в неперекрывающиеся временные периоды
  2. единственным средством передачи информации между шагами является явно определённое состояние (в данном случае переменная state)

Программа с явно заданной таблицей переходов

Конечный автомат, как известно, может быть задан и таблицей переходов. Вообще говоря, код программы, моделирующей конечный автомат, вполне может отражать и это свойство автомата. В следующей программе массив the_table задаёт таблицу переходов. Строки таблицы соответствуют трём состояниям автомата, столбцы — читаемым символам (первый столбец — пробел, второй столбец — перевод строки, третий столбец — все остальные символы). Каждая ячейка таблицы содержит номер нового состояния и признак необходимости печати символа (в приведённом коде используются битовые поля для экономии памяти). Конечно, в реальной задаче могла бы потребоваться гораздо более сложная структура таблицы, содержащая, например, указатели на функции для выполнения каких-либо действий, связанных с переходами, но в рассматриваемом примере это не нужно:

#include <stdio.h>

enum states { before = 0, inside = 1, after = 2 };

typedef struct branch {
  enum states new_state:4;
  int should_putchar:4;
} branch;

branch the_table[3][3] = {
    /*             ' '          '\n'         others      */
    /* before */ { {before, 0}, {before, 1}, {inside, 1} },
    /* inside */ { {after,  0}, {before, 1}, {inside, 1} },
    /* after  */ { {after,  0}, {before, 1}, {after,  0} }
};

void step(enum states *state, int c) {
  int idx2 = (c == ' ') ? 0 : (c == '\n') ? 1 : 2;
  branch *b = & the_table[*state][idx2];

  *state = b->new_state;
  if (b->should_putchar) putchar(c);
}

int main() {
  int c; enum states state = before;

  while ((c = getchar()) != EOF) step(&state, c);

  return 0;
}

Использование объектно-ориентированных возможностей

Если используемый язык программирования поддерживает объектно-ориентированные возможности, логично будет инкапсулировать конечный автомат в объект, скрыв детали реализации. Например, аналогичная программа на языке C++ может выглядеть так:

#include <stdio.h>
class StateMachine {
    enum states { before = 0, inside = 1, after = 2 } state;
    struct branch {
        enum states new_state:4;
        unsigned should_putchar:4;
    };
    static struct branch the_table[3][3];
public:
    StateMachine() : state(before) {}
    void FeedChar(int c) {
        int idx2 = (c == ' ') ? 0 : (c == '\n') ? 1 : 2;
        struct branch *b = & the_table[state][idx2];
        state = b->new_state;
        if(b->should_putchar) putchar(c);
    }
};

struct StateMachine::branch StateMachine::the_table[3][3] = {
    /*             ' '          '\n'         others */
    /* before */ { {before, 0}, {before, 1}, {inside, 1} },
    /* inside */ { {after,  0}, {before, 1}, {inside, 1} },
    /* after  */ { {after,  0}, {before, 1}, {after,  0} }
};

int main()
{
    int c;
    StateMachine machine;
    while((c = getchar()) != EOF)
        machine.FeedChar(c);
    return 0;
}

Отметим, что в этом примере мы использовали для ввода-вывода библиотеку языка Си, чтобы избежать появления «лишних» (отвлекающих внимание) изменений в сравнении с предыдущим примером.

Сфера применения

Автоматное программирование широко применяется при построении лексических анализаторов (классические конечные автоматы) и синтаксических анализаторов (автоматы с магазинной памятью)[1].

Кроме того, мышление в терминах конечных автоматов (то есть разбиение исполнения программы на шаги автомата и передача информации от шага к шагу через состояние) необходимо при построении событийно-ориентированных приложений. В этом случае программирование в стиле конечных автоматов оказывается единственной альтернативой порождению множества процессов или потоков управления (тредов).

Часто понятие состояний и машин состояний используется для спецификации программ. Так, при проектировании программного обеспечения с помощью UML для описания поведения объектов используются диаграммы состояний (state machine diagrams). Кроме того, явное выделение состояний используется в описании сетевых протоколов (см., например, RFC 793[2]).

Мышление в терминах автоматов (шагов и состояний) находит применение и при описании семантики некоторых языков программирования. Так, исполнение программы на языке Рефал представляет собой последовательность изменений поля зрения Рефал-машины или, иначе говоря, последовательность шагов Рефал-автомата, состоянием которого является содержимое поля зрения (произвольное Рефал-выражение, не содержащее переменных).

Механизм продолжений языка Scheme для своей реализации также требует мышления в терминах состояний и шагов, несмотря на то что сам язык Scheme никоим образом не является автоматным. Тем не менее, чтобы обеспечить возможность «замораживания» продолжения, приходится при реализации вычислительной модели языка Scheme объединять все компоненты среды исполнения, включая список действий, которые осталось выполнить для окончания вычислений, в единое целое, которое также обычно называется продолжением. Такое продолжение оказывается состоянием автомата, а процесс выполнения программы состоит из шагов, каждый из которых выводит следующее значение продолжения из предыдущего.

Александр Оллонгрен в своей книге[3] описывает так называемый Венский метод описания семантики языков программирования, основанный целиком на формальных автоматах.

В качестве одного из примеров применения автоматной парадигмы можно назвать систему STAT [www.cs.ucsb.edu/~seclab/projects/stat/index.html]; эта система, в частности, включает встроенный язык STATL, имеющий чисто автоматную семантику.

Существуют также предложения по использованию автоматного программирования в качестве универсального подхода к созданию компьютерных программ вне зависимости от предметной области. Так, авторы статьи[4] утверждают, что автоматное программирование способно сыграть роль легендарной серебряной пули.

История

Наиболее ранние случаи применения парадигмы автоматного программирования относятся, по-видимому, к предметным областям, в которых наработана алгоритмическая теория, основанная на теории автоматов, и прежде всего — к анализу текстов на формальных языках.[1] В качестве одной из наиболее ранних работ на эту тему можно назвать статью.[5]

Одним из первых упоминаний использования техники автоматного программирования независимо от теоретических наработок, основанных на конечных автоматах, является статья Питера Наура.[6] В этой статье автор называет применённый подход «подходом машины Тьюринга» (Turing machine approach), но реально никакая машина Тьюринга в статье не строится; приведённый в статье подход удовлетворяет вышеприведённому определению автоматного программирования.

Сравнение с императивным и процедурным программированием

Понятие состояния программы не является эксклюзивной особенностью автоматного программирования. Вообще говоря, состояние возникает при выполнении любой компьютерной программы и представляет собой совокупность всей информации, которая во время исполнения может изменяться. Так, состояние программы, выполненной в традиционном императивном стиле состоит из

  1. совокупности значений всех глобальных переменных и содержимого динамической памяти
  2. содержимого регистров общего назначения
  3. содержимого стека (включая адреса возвратов и значения локальных переменных)
  4. текущего значения счётчика команд (то есть текущей позиции в коде программы)

Составные части состояния можно разделить на явные (такие как значения переменных) и неявные (адреса возвратов и значение счётчика команд).

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

Связь с объектно-ориентированным программированием

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

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

С другой стороны, очевидно, что понятие объекта представляет собой удачный инструмент реализации модели конечного автомата. При применении парадигмы автоматного программирования в объектно-ориентированных языках обычно модели автоматов представляются в виде классов, состояние автомата описывается внутренними (закрытыми) полями класса, а код шага автомата оформляется в виде метода класса, причём такой метод скорее всего оказывается единственным открытым методом (не считая конструкторов и деструкторов), изменяющим состояние автомата. Другие открытые методы могут служить для получения информации о состоянии автомата, но не меняют его. Все вспомогательные методы (например, методы-обработчики отдельных состояний или их категорий) в таких случаях обычно убирают в закрытую часть класса.

Специализированные языки программирования

В SFC программа описывается в виде схематической последовательности шагов, объединенных переходами.

  • Дракон-схемы — графический язык программирования, используется для программирования в ракетно-космической технике («Буран», «Морской старт», «Тополь»). Существует бесплатный Дракон-редактор.
  • [reflex-language.narod.ru/ Язык Рефлекс] — Си-подобный язык программирования, ориентированный на описание сложных алгоритмов управления в задачах промышленной автоматизации.

Напишите отзыв о статье "Автоматное программирование"

Примечания

  1. 1 2 А. Ахо, Дж. Ульман. Теория синтаксического анализа, перевода и компиляции = The theory of parsing, translation and compiling. — М.: МИР, 1978. — Т. 1. — 612 с.
  2. Postel, J., ed., Transmission Control Protocol, RFC 793
  3. А. Оллонгрен. Определение языков программирования интерпретирующими автоматами = Definition of programming languages by interpreting automata. — М.: МИР, 1977. — 288 с.
  4. Туккель Н.И., Шалыто А.А. [is.ifmo.ru/works/mirk/ Программирование с явным выделением состояний] // Мир ПК. — 2001. — № 9. — С. 132—138.
  5. Johnson, W. L.; Porter, J. H.; Ackley, S. I.; Ross, D. T. (1968). «Automatic generation of efficient lexical processors using finite state techniques» (English). Comm. ACM 11 (12): 805—813.
  6. Naur, Peter (September 1963). «The design of the GIER ALGOL compiler Part II» (English). BIT Numerical Mathematics 3: 145-166. DOI:10.1007/BF01939983. ISSN [worldcat.org/issn/0006-3835 0006-3835].

Литература

  • Поликарпова Н.И., Шалыто А.А. [project.ifmo.ru/books/2 Автоматное программирование][is.ifmo.ru/books/_book.pdf]. — СПб.: Питер, 2009. — 176 с. — ISBN 978-5-388-00692-9.
  • Шалыто А.А. [project.ifmo.ru/books/1 Switch-технология. Алгоритмизация и программирование задач логического управления.]. — СПб.: Наука, 1998. — 628 с.
  • [www.amazon.com/Practical-UML-Statecharts-Second-Event-Driven/dp/0750687061/ref=ntt_at_ep_dpi_1 Practical UML Statecharts in C/C++] Книга о реализации UML2 State Machine на С/C++. Нацелена, главным образом, на программирование встроенных систем реального времени.
  • Научно-технический вестник СПбГУ ИТМО. Выпуск 53. Автоматное программирование. 2008. ntv.ifmo.ru/file/journal/61.pdf
  • Зюбин В. Е. [softcraft.ru/auto/etc/mpz/index.shtml Программирование информационно-управляющих систем на основе конечных автоматов: учебное пособие.]. — Новосибирск: Изд-во: Новосиб. гос. ун-т, 2006. — 96 с. — ISBN 5-94356-425-Х.
  • Непейвода Н.Н. Стили и методы программирования. курс лекций. учебное пособие. — М.: Интернет-университет информационных технологий, 2005. — С. 145—212. — 316 с. — ISBN 5-9556-0023-X.
  • Harel, David (1987). «[www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf Statecharts: A Visual Formalism for Complex Systems]». Sci. Comput. Programming (8): 231—274.
  • Harel, David; Drusinsky, D. (1989). «Using Statecharts for Hardware Description and Synthesis». IEEE Trans. Computer Aided Design of Integrated Circuits and Systems (8): 798—807.

Ссылки

  • [project.ifmo.ru/ «Автоматное программирование. Проекты» (архив проектов с открытой документацией)]
  • [www.softcraft.ru/design/ap/ap01.shtml Б. П. Кузнецов. «Психология автоматного программирования»]
  • [www.softcraft.ru/auto/etc/itst/index.shtml Б. П. Кузнецов. «Настраиваемые автоматные программы»]

См. также

Отрывок, характеризующий Автоматное программирование

Гончих соединили в одну стаю, и дядюшка с Николаем поехали рядом. Наташа, закутанная платками, из под которых виднелось оживленное с блестящими глазами лицо, подскакала к ним, сопутствуемая не отстававшими от нее Петей и Михайлой охотником и берейтором, который был приставлен нянькой при ней. Петя чему то смеялся и бил, и дергал свою лошадь. Наташа ловко и уверенно сидела на своем вороном Арабчике и верной рукой, без усилия, осадила его.
Дядюшка неодобрительно оглянулся на Петю и Наташу. Он не любил соединять баловство с серьезным делом охоты.
– Здравствуйте, дядюшка, и мы едем! – прокричал Петя.
– Здравствуйте то здравствуйте, да собак не передавите, – строго сказал дядюшка.
– Николенька, какая прелестная собака, Трунила! он узнал меня, – сказала Наташа про свою любимую гончую собаку.
«Трунила, во первых, не собака, а выжлец», подумал Николай и строго взглянул на сестру, стараясь ей дать почувствовать то расстояние, которое должно было их разделять в эту минуту. Наташа поняла это.
– Вы, дядюшка, не думайте, чтобы мы помешали кому нибудь, – сказала Наташа. Мы станем на своем месте и не пошевелимся.
– И хорошее дело, графинечка, – сказал дядюшка. – Только с лошади то не упадите, – прибавил он: – а то – чистое дело марш! – не на чем держаться то.
Остров отрадненского заказа виднелся саженях во ста, и доезжачие подходили к нему. Ростов, решив окончательно с дядюшкой, откуда бросать гончих и указав Наташе место, где ей стоять и где никак ничего не могло побежать, направился в заезд над оврагом.
– Ну, племянничек, на матерого становишься, – сказал дядюшка: чур не гладить (протравить).
– Как придется, отвечал Ростов. – Карай, фюит! – крикнул он, отвечая этим призывом на слова дядюшки. Карай был старый и уродливый, бурдастый кобель, известный тем, что он в одиночку бирал матерого волка. Все стали по местам.
Старый граф, зная охотничью горячность сына, поторопился не опоздать, и еще не успели доезжачие подъехать к месту, как Илья Андреич, веселый, румяный, с трясущимися щеками, на своих вороненьких подкатил по зеленям к оставленному ему лазу и, расправив шубку и надев охотничьи снаряды, влез на свою гладкую, сытую, смирную и добрую, поседевшую как и он, Вифлянку. Лошадей с дрожками отослали. Граф Илья Андреич, хотя и не охотник по душе, но знавший твердо охотничьи законы, въехал в опушку кустов, от которых он стоял, разобрал поводья, оправился на седле и, чувствуя себя готовым, оглянулся улыбаясь.
Подле него стоял его камердинер, старинный, но отяжелевший ездок, Семен Чекмарь. Чекмарь держал на своре трех лихих, но также зажиревших, как хозяин и лошадь, – волкодавов. Две собаки, умные, старые, улеглись без свор. Шагов на сто подальше в опушке стоял другой стремянной графа, Митька, отчаянный ездок и страстный охотник. Граф по старинной привычке выпил перед охотой серебряную чарку охотничьей запеканочки, закусил и запил полубутылкой своего любимого бордо.
Илья Андреич был немножко красен от вина и езды; глаза его, подернутые влагой, особенно блестели, и он, укутанный в шубку, сидя на седле, имел вид ребенка, которого собрали гулять. Худой, со втянутыми щеками Чекмарь, устроившись с своими делами, поглядывал на барина, с которым он жил 30 лет душа в душу, и, понимая его приятное расположение духа, ждал приятного разговора. Еще третье лицо подъехало осторожно (видно, уже оно было учено) из за леса и остановилось позади графа. Лицо это был старик в седой бороде, в женском капоте и высоком колпаке. Это был шут Настасья Ивановна.
– Ну, Настасья Ивановна, – подмигивая ему, шопотом сказал граф, – ты только оттопай зверя, тебе Данило задаст.
– Я сам… с усам, – сказал Настасья Ивановна.
– Шшшш! – зашикал граф и обратился к Семену.
– Наталью Ильиничну видел? – спросил он у Семена. – Где она?
– Они с Петром Ильичем от Жаровых бурьяно встали, – отвечал Семен улыбаясь. – Тоже дамы, а охоту большую имеют.
– А ты удивляешься, Семен, как она ездит… а? – сказал граф, хоть бы мужчине в пору!
– Как не дивиться? Смело, ловко.
– А Николаша где? Над Лядовским верхом что ль? – всё шопотом спрашивал граф.
– Так точно с. Уж они знают, где стать. Так тонко езду знают, что мы с Данилой другой раз диву даемся, – говорил Семен, зная, чем угодить барину.
– Хорошо ездит, а? А на коне то каков, а?
– Картину писать! Как намеднись из Заварзинских бурьянов помкнули лису. Они перескакивать стали, от уймища, страсть – лошадь тысяча рублей, а седоку цены нет. Да уж такого молодца поискать!
– Поискать… – повторил граф, видимо сожалея, что кончилась так скоро речь Семена. – Поискать? – сказал он, отворачивая полы шубки и доставая табакерку.
– Намедни как от обедни во всей регалии вышли, так Михаил то Сидорыч… – Семен не договорил, услыхав ясно раздававшийся в тихом воздухе гон с подвыванием не более двух или трех гончих. Он, наклонив голову, прислушался и молча погрозился барину. – На выводок натекли… – прошептал он, прямо на Лядовской повели.
Граф, забыв стереть улыбку с лица, смотрел перед собой вдаль по перемычке и, не нюхая, держал в руке табакерку. Вслед за лаем собак послышался голос по волку, поданный в басистый рог Данилы; стая присоединилась к первым трем собакам и слышно было, как заревели с заливом голоса гончих, с тем особенным подвыванием, которое служило признаком гона по волку. Доезжачие уже не порскали, а улюлюкали, и из за всех голосов выступал голос Данилы, то басистый, то пронзительно тонкий. Голос Данилы, казалось, наполнял весь лес, выходил из за леса и звучал далеко в поле.
Прислушавшись несколько секунд молча, граф и его стремянной убедились, что гончие разбились на две стаи: одна большая, ревевшая особенно горячо, стала удаляться, другая часть стаи понеслась вдоль по лесу мимо графа, и при этой стае было слышно улюлюканье Данилы. Оба эти гона сливались, переливались, но оба удалялись. Семен вздохнул и нагнулся, чтоб оправить сворку, в которой запутался молодой кобель; граф тоже вздохнул и, заметив в своей руке табакерку, открыл ее и достал щепоть. «Назад!» крикнул Семен на кобеля, который выступил за опушку. Граф вздрогнул и уронил табакерку. Настасья Ивановна слез и стал поднимать ее.
Граф и Семен смотрели на него. Вдруг, как это часто бывает, звук гона мгновенно приблизился, как будто вот, вот перед ними самими были лающие рты собак и улюлюканье Данилы.
Граф оглянулся и направо увидал Митьку, который выкатывавшимися глазами смотрел на графа и, подняв шапку, указывал ему вперед, на другую сторону.
– Береги! – закричал он таким голосом, что видно было, что это слово давно уже мучительно просилось у него наружу. И поскакал, выпустив собак, по направлению к графу.
Граф и Семен выскакали из опушки и налево от себя увидали волка, который, мягко переваливаясь, тихим скоком подскакивал левее их к той самой опушке, у которой они стояли. Злобные собаки визгнули и, сорвавшись со свор, понеслись к волку мимо ног лошадей.
Волк приостановил бег, неловко, как больной жабой, повернул свою лобастую голову к собакам, и также мягко переваливаясь прыгнул раз, другой и, мотнув поленом (хвостом), скрылся в опушку. В ту же минуту из противоположной опушки с ревом, похожим на плач, растерянно выскочила одна, другая, третья гончая, и вся стая понеслась по полю, по тому самому месту, где пролез (пробежал) волк. Вслед за гончими расступились кусты орешника и показалась бурая, почерневшая от поту лошадь Данилы. На длинной спине ее комочком, валясь вперед, сидел Данила без шапки с седыми, встрепанными волосами над красным, потным лицом.
– Улюлюлю, улюлю!… – кричал он. Когда он увидал графа, в глазах его сверкнула молния.
– Ж… – крикнул он, грозясь поднятым арапником на графа.
– Про…ли волка то!… охотники! – И как бы не удостоивая сконфуженного, испуганного графа дальнейшим разговором, он со всей злобой, приготовленной на графа, ударил по ввалившимся мокрым бокам бурого мерина и понесся за гончими. Граф, как наказанный, стоял оглядываясь и стараясь улыбкой вызвать в Семене сожаление к своему положению. Но Семена уже не было: он, в объезд по кустам, заскакивал волка от засеки. С двух сторон также перескакивали зверя борзятники. Но волк пошел кустами и ни один охотник не перехватил его.


Николай Ростов между тем стоял на своем месте, ожидая зверя. По приближению и отдалению гона, по звукам голосов известных ему собак, по приближению, отдалению и возвышению голосов доезжачих, он чувствовал то, что совершалось в острове. Он знал, что в острове были прибылые (молодые) и матерые (старые) волки; он знал, что гончие разбились на две стаи, что где нибудь травили, и что что нибудь случилось неблагополучное. Он всякую секунду на свою сторону ждал зверя. Он делал тысячи различных предположений о том, как и с какой стороны побежит зверь и как он будет травить его. Надежда сменялась отчаянием. Несколько раз он обращался к Богу с мольбою о том, чтобы волк вышел на него; он молился с тем страстным и совестливым чувством, с которым молятся люди в минуты сильного волнения, зависящего от ничтожной причины. «Ну, что Тебе стоит, говорил он Богу, – сделать это для меня! Знаю, что Ты велик, и что грех Тебя просить об этом; но ради Бога сделай, чтобы на меня вылез матерый, и чтобы Карай, на глазах „дядюшки“, который вон оттуда смотрит, влепился ему мертвой хваткой в горло». Тысячу раз в эти полчаса упорным, напряженным и беспокойным взглядом окидывал Ростов опушку лесов с двумя редкими дубами над осиновым подседом, и овраг с измытым краем, и шапку дядюшки, чуть видневшегося из за куста направо.
«Нет, не будет этого счастья, думал Ростов, а что бы стоило! Не будет! Мне всегда, и в картах, и на войне, во всем несчастье». Аустерлиц и Долохов ярко, но быстро сменяясь, мелькали в его воображении. «Только один раз бы в жизни затравить матерого волка, больше я не желаю!» думал он, напрягая слух и зрение, оглядываясь налево и опять направо и прислушиваясь к малейшим оттенкам звуков гона. Он взглянул опять направо и увидал, что по пустынному полю навстречу к нему бежало что то. «Нет, это не может быть!» подумал Ростов, тяжело вздыхая, как вздыхает человек при совершении того, что было долго ожидаемо им. Совершилось величайшее счастье – и так просто, без шума, без блеска, без ознаменования. Ростов не верил своим глазам и сомнение это продолжалось более секунды. Волк бежал вперед и перепрыгнул тяжело рытвину, которая была на его дороге. Это был старый зверь, с седою спиной и с наеденным красноватым брюхом. Он бежал не торопливо, очевидно убежденный, что никто не видит его. Ростов не дыша оглянулся на собак. Они лежали, стояли, не видя волка и ничего не понимая. Старый Карай, завернув голову и оскалив желтые зубы, сердито отыскивая блоху, щелкал ими на задних ляжках.
– Улюлюлю! – шопотом, оттопыривая губы, проговорил Ростов. Собаки, дрогнув железками, вскочили, насторожив уши. Карай почесал свою ляжку и встал, насторожив уши и слегка мотнул хвостом, на котором висели войлоки шерсти.
– Пускать – не пускать? – говорил сам себе Николай в то время как волк подвигался к нему, отделяясь от леса. Вдруг вся физиономия волка изменилась; он вздрогнул, увидав еще вероятно никогда не виданные им человеческие глаза, устремленные на него, и слегка поворотив к охотнику голову, остановился – назад или вперед? Э! всё равно, вперед!… видно, – как будто сказал он сам себе, и пустился вперед, уже не оглядываясь, мягким, редким, вольным, но решительным скоком.
– Улюлю!… – не своим голосом закричал Николай, и сама собою стремглав понеслась его добрая лошадь под гору, перескакивая через водомоины в поперечь волку; и еще быстрее, обогнав ее, понеслись собаки. Николай не слыхал своего крика, не чувствовал того, что он скачет, не видал ни собак, ни места, по которому он скачет; он видел только волка, который, усилив свой бег, скакал, не переменяя направления, по лощине. Первая показалась вблизи зверя чернопегая, широкозадая Милка и стала приближаться к зверю. Ближе, ближе… вот она приспела к нему. Но волк чуть покосился на нее, и вместо того, чтобы наддать, как она это всегда делала, Милка вдруг, подняв хвост, стала упираться на передние ноги.
– Улюлюлюлю! – кричал Николай.
Красный Любим выскочил из за Милки, стремительно бросился на волка и схватил его за гачи (ляжки задних ног), но в ту ж секунду испуганно перескочил на другую сторону. Волк присел, щелкнул зубами и опять поднялся и поскакал вперед, провожаемый на аршин расстояния всеми собаками, не приближавшимися к нему.
– Уйдет! Нет, это невозможно! – думал Николай, продолжая кричать охрипнувшим голосом.
– Карай! Улюлю!… – кричал он, отыскивая глазами старого кобеля, единственную свою надежду. Карай из всех своих старых сил, вытянувшись сколько мог, глядя на волка, тяжело скакал в сторону от зверя, наперерез ему. Но по быстроте скока волка и медленности скока собаки было видно, что расчет Карая был ошибочен. Николай уже не далеко впереди себя видел тот лес, до которого добежав, волк уйдет наверное. Впереди показались собаки и охотник, скакавший почти на встречу. Еще была надежда. Незнакомый Николаю, муругий молодой, длинный кобель чужой своры стремительно подлетел спереди к волку и почти опрокинул его. Волк быстро, как нельзя было ожидать от него, приподнялся и бросился к муругому кобелю, щелкнул зубами – и окровавленный, с распоротым боком кобель, пронзительно завизжав, ткнулся головой в землю.
– Караюшка! Отец!.. – плакал Николай…
Старый кобель, с своими мотавшимися на ляжках клоками, благодаря происшедшей остановке, перерезывая дорогу волку, был уже в пяти шагах от него. Как будто почувствовав опасность, волк покосился на Карая, еще дальше спрятав полено (хвост) между ног и наддал скоку. Но тут – Николай видел только, что что то сделалось с Караем – он мгновенно очутился на волке и с ним вместе повалился кубарем в водомоину, которая была перед ними.
Та минута, когда Николай увидал в водомоине копошащихся с волком собак, из под которых виднелась седая шерсть волка, его вытянувшаяся задняя нога, и с прижатыми ушами испуганная и задыхающаяся голова (Карай держал его за горло), минута, когда увидал это Николай, была счастливейшею минутою его жизни. Он взялся уже за луку седла, чтобы слезть и колоть волка, как вдруг из этой массы собак высунулась вверх голова зверя, потом передние ноги стали на край водомоины. Волк ляскнул зубами (Карай уже не держал его за горло), выпрыгнул задними ногами из водомоины и, поджав хвост, опять отделившись от собак, двинулся вперед. Карай с ощетинившейся шерстью, вероятно ушибленный или раненый, с трудом вылезал из водомоины.
– Боже мой! За что?… – с отчаянием закричал Николай.
Охотник дядюшки с другой стороны скакал на перерез волку, и собаки его опять остановили зверя. Опять его окружили.
Николай, его стремянной, дядюшка и его охотник вертелись над зверем, улюлюкая, крича, всякую минуту собираясь слезть, когда волк садился на зад и всякий раз пускаясь вперед, когда волк встряхивался и подвигался к засеке, которая должна была спасти его. Еще в начале этой травли, Данила, услыхав улюлюканье, выскочил на опушку леса. Он видел, как Карай взял волка и остановил лошадь, полагая, что дело было кончено. Но когда охотники не слезли, волк встряхнулся и опять пошел на утек. Данила выпустил своего бурого не к волку, а прямой линией к засеке так же, как Карай, – на перерез зверю. Благодаря этому направлению, он подскакивал к волку в то время, как во второй раз его остановили дядюшкины собаки.
Данила скакал молча, держа вынутый кинжал в левой руке и как цепом молоча своим арапником по подтянутым бокам бурого.
Николай не видал и не слыхал Данилы до тех пор, пока мимо самого его не пропыхтел тяжело дыша бурый, и он услыхал звук паденья тела и увидал, что Данила уже лежит в середине собак на заду волка, стараясь поймать его за уши. Очевидно было и для собак, и для охотников, и для волка, что теперь всё кончено. Зверь, испуганно прижав уши, старался подняться, но собаки облепили его. Данила, привстав, сделал падающий шаг и всей тяжестью, как будто ложась отдыхать, повалился на волка, хватая его за уши. Николай хотел колоть, но Данила прошептал: «Не надо, соструним», – и переменив положение, наступил ногою на шею волку. В пасть волку заложили палку, завязали, как бы взнуздав его сворой, связали ноги, и Данила раза два с одного бока на другой перевалил волка.
С счастливыми, измученными лицами, живого, матерого волка взвалили на шарахающую и фыркающую лошадь и, сопутствуемые визжавшими на него собаками, повезли к тому месту, где должны были все собраться. Молодых двух взяли гончие и трех борзые. Охотники съезжались с своими добычами и рассказами, и все подходили смотреть матёрого волка, который свесив свою лобастую голову с закушенною палкой во рту, большими, стеклянными глазами смотрел на всю эту толпу собак и людей, окружавших его. Когда его трогали, он, вздрагивая завязанными ногами, дико и вместе с тем просто смотрел на всех. Граф Илья Андреич тоже подъехал и потрогал волка.
– О, материщий какой, – сказал он. – Матёрый, а? – спросил он у Данилы, стоявшего подле него.
– Матёрый, ваше сиятельство, – отвечал Данила, поспешно снимая шапку.
Граф вспомнил своего прозеванного волка и свое столкновение с Данилой.
– Однако, брат, ты сердит, – сказал граф. – Данила ничего не сказал и только застенчиво улыбнулся детски кроткой и приятной улыбкой.


Старый граф поехал домой; Наташа с Петей обещались сейчас же приехать. Охота пошла дальше, так как было еще рано. В середине дня гончих пустили в поросший молодым частым лесом овраг. Николай, стоя на жнивье, видел всех своих охотников.
Насупротив от Николая были зеленя и там стоял его охотник, один в яме за выдавшимся кустом орешника. Только что завели гончих, Николай услыхал редкий гон известной ему собаки – Волторна; другие собаки присоединились к нему, то замолкая, то опять принимаясь гнать. Через минуту подали из острова голос по лисе, и вся стая, свалившись, погнала по отвершку, по направлению к зеленям, прочь от Николая.
Он видел скачущих выжлятников в красных шапках по краям поросшего оврага, видел даже собак, и всякую секунду ждал того, что на той стороне, на зеленях, покажется лисица.
Охотник, стоявший в яме, тронулся и выпустил собак, и Николай увидал красную, низкую, странную лисицу, которая, распушив трубу, торопливо неслась по зеленям. Собаки стали спеть к ней. Вот приблизились, вот кругами стала вилять лисица между ними, всё чаще и чаще делая эти круги и обводя вокруг себя пушистой трубой (хвостом); и вот налетела чья то белая собака, и вслед за ней черная, и всё смешалось, и звездой, врозь расставив зады, чуть колеблясь, стали собаки. К собакам подскакали два охотника: один в красной шапке, другой, чужой, в зеленом кафтане.