Изучите awk, написав игру «Угадай число».
Языки программирования, как правило, имеют много общих черт. Отличный способ выучить новый язык — создать знакомую программу. В этой статье я создам игру «Угадай число», используя awk для демонстрации знакомых концепций.
Когда вы изучаете новый язык программирования, полезно сосредоточиться на общих чертах большинства языков программирования:
- Переменные – места, где хранится информация.
- Выражения – способы вычисления вещей
- Заявления - средства, с помощью которых изменения состояния выражаются в программе.
Эти концепции лежат в основе большинства языков программирования.
Как только вы поймете эти концепции, вы сможете начать разбираться со всем остальным. Например, в большинстве языков есть «способ ведения дел», поддерживаемый их дизайном, и эти способы могут сильно отличаться от одной программы к другой. Эти способы включают модульность (группирование связанных функций вместе), декларативный или императивный, объектно-ориентированный, синтаксические функции низкого и высокого уровня и т. д. Примером, знакомым многим программистам, является «церемония», то есть объем работы, необходимый для подготовки сцены перед решением проблемы. Говорят, что к языку программирования Java предъявляются серьезные требования, связанные с его дизайном, который требует, чтобы весь код был определен внутри класса.
Но вернемся к основам. Языки программирования обычно имеют сходство. Как только вы освоите один язык программирования, начните с изучения основ другого, чтобы оценить различия в этом новом языке.
Хороший способ продолжить — создать набор базовых тестовых программ. Имея это в виду, обучение начинается с этих сходств.
Одна из тестовых программ, которую вы можете использовать, — это программа «Угадай число». Компьютер выбирает число от одного до ста и просит вас угадать это число. Программа работает до тех пор, пока вы не сделаете правильное предположение.
Программа «Угадай число» использует несколько концепций языков программирования:
- Переменные
- Вход
- Выход
- Условная оценка
- Петли
Это отличный практический эксперимент по изучению нового языка программирования.
Примечание. Эта статья адаптирована из статьи Моше Задки об использовании этого подхода в Джулии и статьи Джима Холла об использовании этого подхода в Bash.
Угадай число в awk
Давайте напишем игру «Угадай число» в виде программы на Awk.
Awk является динамически типизированным, представляет собой язык сценариев, ориентированный на преобразование данных, и имеет на удивление хорошую поддержку интерактивного использования. Awk существует с 1970-х годов, первоначально как часть операционной системы Unix. Если вы не знаете Awk, но любите электронные таблицы, это знак… идите изучать Awk!
Вы можете начать изучение Awk с написания версии игры «Угадай число».
Вот моя реализация (с номерами строк, чтобы мы могли рассмотреть некоторые особенности):
1 BEGIN {
2 srand(42)
3 randomNumber = int(rand() * 100) + 1
4 print "random number is",randomNumber
5 printf "guess a number between 1 and 100\n"
6 }
7 {
8 guess = int($0)
9 if (guess < randomNumber) {
10 printf "too low, try again:"
11 } else if (guess > randomNumber) {
12 printf "too high, try again:"
13 } else {
14 printf "that's right\n"
15 exit
16 }
17 }
Мы сразу можем увидеть сходство между структурами управления Awk и структурами C или Java, но в отличие от Python. В таких операторах, как if-then-else или while, then, else и while Части принимают либо оператор, либо группу операторов, заключенных в { и . Однако есть одно большое отличие AWk, которое необходимо понимать с самого начала:
По своей конструкции Awk построен на конвейере данных.
Что это значит? Большинство программ Awk представляют собой фрагменты кода, которые получают строку ввода, что-то делают с данными и записывают их на вывод. Признавая необходимость такого конвейера преобразования, Awk по умолчанию предоставляет все необходимые инструменты преобразования. Давайте исследуем это с помощью приведенной выше программы, задав основной вопрос: где находится структура «чтения данных из консоли»?
Ответ на этот вопрос – он встроен. В частности, строки 7–17 сообщают Awk, что делать с каждой прочитанной строкой. Учитывая этот контекст, довольно легко увидеть, что строки 1–6 выполняются до того, как что-либо будет прочитано.
Точнее, ключевое слово BEGIN в строке 1 представляет собой своего рода «шаблон», в данном случае указывающий Awk, что перед чтением каких-либо данных он должен выполнить то, что следует за BEGIN. > в {… }. Аналогичное ключевое слово END, не используемое в этой программе, указывает Awk, что делать, когда все прочитано.
Возвращаясь к строкам 7–17, мы видим, что они создают похожий блок кода {…}, но перед ним нет ключевого слова. Поскольку перед { Awk нет ничего, что могло бы сопоставиться, он будет применять эту строку к каждой полученной строке ввода. Каждая строка ввода будет введена пользователем как предположение.
Давайте посмотрим на выполняемый код. Во-первых, преамбула, которая происходит перед чтением любого ввода.
В строке 2 мы инициализируем генератор случайных чисел числом 42 (если мы не указываем аргумент, используются системные часы). 42? Конечно, 42. Строка 3 вычисляет случайное число от 1 до 100, а строка 4 выводит это число в целях отладки. Строка 5 предлагает пользователю угадать число. Обратите внимание, что в этой строке используется printf
, а не print
. Как и в C, первый аргумент printf
— это шаблон, используемый для форматирования вывода.
Теперь, когда пользователь знает, что программа ожидает ввода, он может ввести предположение на консоли. Awk передает это предположение в код в строках 7–17, как упоминалось ранее. Строка 18 преобразует входную запись в целое число; $0
указывает всю входную запись, тогда как $1
указывает первое поле входной записи, $2
второй и так далее. Да, Awk разбивает строку ввода на составляющие поля, используя предопределенный разделитель, который по умолчанию равен пробелу. Строки 9–15 сравнивают предположение со случайным числом, печатая соответствующие ответы. Если предположение верно, строка 15 преждевременно выходит из конвейера обработки входной строки.
Простой!
Учитывая необычную структуру программ Awk как фрагментов кода, которые реагируют на определенные конфигурации входных строк и выполняют различные действия с данными, давайте рассмотрим альтернативную структуру, чтобы увидеть, как работает фильтрующая часть:
1 BEGIN {
2 srand(42)
3 randomNumber = int(rand() * 100) + 1
4 print "random number is",randomNumber
5 printf "guess a number between 1 and 100\n"
6 }
7 int($0) < randomNumber {
8 printf "too low, try again: "
9 }
10 int($0) > randomNumber {
11 printf "too high, try again: "
12 }
13 int($0) == randomNumber {
14 printf "that's right\n"
15 exit
16 }
Строки 1–6 не изменились. Но теперь мы видим, что строки 7–9 — это код, который выполняется, когда целое значение строки меньше случайного числа, строки 10–12 — это код, который выполняется, когда целое значение строки больше случайного числа. , а строки 13–16 — это код, который выполняется, когда они совпадают.
Это должно показаться «крутым, но странным» — например, зачем нам постоянно вычислять int($0)
? И наверняка это был бы странный способ решения проблемы. Но эти шаблоны могут быть действительно замечательными способами разделения условной обработки, поскольку они могут использовать регулярные выражения или любую другую структуру, поддерживаемую Awk.
Для полноты мы можем использовать эти шаблоны, чтобы отделить общие вычисления от вещей, применимых только к конкретным обстоятельствам. Вот третья версия для иллюстрации:
1 BEGIN {
2 srand(42)
3 randomNumber = int(rand() * 100) + 1
4 print "random number is",randomNumber
5 printf "guess a number between 1 and 100\n"
6 }
7 {
8 guess = int($0)
9 }
10 guess < randomNumber {
11 printf "too low, try again: "
12 }
13 guess > randomNumber {
14 printf "too high, try again: "
15 }
16 guess == randomNumber {
17 printf "that's right\n"
18 exit
19 }
Понимая, что независимо от того, какое входное значение поступает, его необходимо преобразовать в целое число, мы создали строки 7–9 именно для этого. Теперь три группы строк, 10–12, 13–15 и 16–19, относятся к уже определенной переменной, вместо того, чтобы каждый раз преобразовывать входную строку.
Вернемся к списку того, чему мы хотели научиться:
- переменные — да, они есть в Awk; мы можем сделать вывод, что входные данные поступают в виде строк, но при необходимости могут быть преобразованы в числовое значение.
- ввод – Awk просто отправляет ввод через свой подход «конвейер преобразования данных» для чтения данных.
- вывод — мы использовали процедуры Awk
print
иprintf
для записи данных на вывод. - условная оценка — мы узнали о if-then-else и входных фильтрах Awk, которые реагируют на определенные конфигурации входных строк.
- петли – да, представьте себе! Нам снова не понадобился цикл благодаря подходу «конвейера преобразования данных», который использует Awk; цикл «просто происходит». Обратите внимание, что пользователь может выйти из конвейера преждевременно, отправив сигнал конца файла в Awk (CTRL-D при использовании окна терминала Linux).
Стоит учитывать важность отсутствия цикла для обработки ввода. Одна из причин, по которой Awk так долго оставался жизнеспособным, заключается в том, что программы Awk компактны, а одна из причин их компактности заключается в том, что для чтения из консоли или файла не требуется шаблонный код.
Запустим программу:
$ awk -f guess.awk
random number is 25
guess a number between 1 and 100: 50
too high, try again: 30
too high, try again: 10
too low, try again: 25
that's right
$
Единственное, что мы не рассмотрели, — это комментарии. Комментарий Awk начинается с #
и заканчивается концом строки.
Заворачивать
Awk невероятно мощный инструмент, и эта игра «Угадай число» — отличный способ начать. Однако это не должно быть концом вашего путешествия. Вы можете прочитать об истории Awk and Gawk (GNU Awk), расширенной версии Awk и, вероятно, той, которая установлена на вашем компьютере, если вы используете Linux, или прочитать все об оригинале от его первоначальных разработчиков.
Вы также можете скачать нашу шпаргалку, которая поможет вам отслеживать все, что вы изучаете.