Генерация вывода
Теперь, когда структура данных построена, пора переходить к следующему шагу —
генерации нового текста. Основная идея остается неизменной: у нас есть префикс,
мы случайным образом выбираем один из возможных для него суффиксов, печатаем
его, затем обновляем префикс. Это повторяющаяся часть обработки; нам еще надо
продумать, как начинать и как заканчивать алгоритм. Начать
будет нетрудно, если
мы запомним слова первого префикса и начнем с них. Закончить алгоритм также
нетрудно; для этого нам понадобится слово-маркер. Прочтя весь вводимый текст,
мы можем добавить некий завершитель — "слово", которое с гарантией не
встретится ни в одном тексте:
build(prefix, stdin);
add(prefix, NONWORD);
В этом фрагменте NONWORD — некоторое значение, которое точно никогда
не
встретится в тексте. Поскольку по нашему определению слова разделяются
пробелами, на роль завершителя подойдет "слово", равносильное пробелу, но
отличное от него, например символ перевода строки:
char NONWORD[] = "\n"; /* никогда не встретится */
Еще одна проблема — что делать, если вводимого текста недостаточно для запуска
алгоритма? Для решения этой проблемы существуют два принципиальных подхода
— либо
прерывать работу программы, если введенного текста недостаточно, либо
считать, что для генерации хватит любого фрагмента, и просто не утруждать себя
проверкой. Для данной программы лучше выбрать второй подход.
Можно начать процесс генерации с создания фиктивного префикса, который даст
гарантию, что для работы программы всегда хватит вводимого текста. Для начала
можно
инициализировать все значения массива префиксов словом NONWORD. Это
даст дополнительное преимущество — первое слово во вводимом файле будет
первым суффиксом нашего вымышленного префикса, так что в генерирующем цикле
печатать надо будет только суффиксы.
На случай, если генерируемый текст окажется непредсказуемо большого размера,
можно встроить ограничитель — прерывать алгоритм после вывода заданного
количества слов или
при появлении NONWORD в качестве суффикса.
Добавление нескольких NONWORD в концы структур данных значительно упрощает
основные циклы программы — это хороший пример использования специальных
значений для маркировки границ — сигнальных меток (sentinel).
Как правило, надо стараться обработать все отклонения, исключения и особые
случаи непосредственно в данных. Код писать труднее, так что старайтесь добиться
того, чтобы
управляющая логика была как можно более проста и прямолинейна.
Функция generate использует алгоритм, который мы только что описали в общих
словах. Она генерирует текст по слову в строке; эти слова можно группировать в
более длинные строки при помощи любого текстового редактора — в главе 9 будет
показано простое средство для такого форматирования — процедура f mt.
Благодаря
использованию в начале и в конце строк NONWORD, generate начинает и
заканчивает работу без проблем: