Руководство пользователя для GNU Awk

Arnold D. Robbins
перевод Балуева А. Н.

7. Выражения

Оглавление
В начало страницы

7.8 Операторы увеличения и уменьшения

В начало страницы

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

Оператор добавления единицы пишется `++'. Он может использоваться для увеличения переменной до или после использования ее значения. Для предварительного увеличения переменной v нужно писать `++v'. Это добавляет единицу к значению v и это новое значение есть значение выражения. Этот оператор эквивалентен оператору `v += 1'.

Запись `++' после переменной определяет пост-приращение. Такой оператор также увеличивает значение переменной. Разница в том, что значением увеличенного выражения считается старое значение переменной. Так, если foo имеет значение 4, то выражение `foo++' также имеет значение 4, но меняет значение foo на 5.

Пост-приращение `foo++' почти эквивалентно `(foo += 1) - 1'. Они не точно эквивалентны потому, что все числа в awk обрабатываются с плавающей точкой. Поэтому `foo + 1 - 1' не обязательно равно foo. Но эта разность ощутима только если вы имеете дело с очень маленькими числами (меньше чем 10e12).

Любое lvalue может быть увеличено на единицу. Поля и элементы массивов увеличиваются подобно переменным. (Пишите `$(i++)' если вы хотите сослаться на поле и одновременно увеличить переменную. Скобки необходимы ввиду большего приоритета оператора ссылки на поле `$'.)

Оператор уменьшения `--' действует подобно `++', вычитая единицу вместо добавления. Подобно `++', он может действовать как преуменьшение и пост-уменьшение lvalue. Приведем сводку этих операторов.

++lvalue Это выражение увеличивает lvalue и новое значение становится значением этого выражения.

lvalue++ Это выражение увеличивает lvalue, но значение выражения есть старое значение lvalue.

--lvalue Подобно `++lvalue', но вместо увеличения вычитает единицу. Оно уменьшает lvalue и новое значение считается также значением выражения.

lvalue-- Подобно `lvalue++', но вместо добавления вычитает единицу, уменьшая lvalue. Значение выражения есть старое значение lvalue.

7.9 True и False в awk

В начало страницы

Многие языки программирования имеют специальные представления для понятий "true" и "false." Они обычно употребляют специальные постоянные true и false, или их эквиваленты на верхнем регистре. В awk это не так. Он заимствует обозначение у Си. В awk, каждое ненулевое численное значение или непустая цепочка имеют значение true. Всякое другое значение (т.е. 0 или пустая цепочка, "") есть false. Следующая программа будет печатать `A strange truth value' три раза:


BEGIN -
if (3.1415927)
print "A strange truth value" 
	if ("Four Score And Seven Years Ago")
print "A strange truth value" if (j = 57)
print "A strange truth value" ""

Имеется неожиданное следствие правила "не ноль или не пусто" : строковая константа "0" есть true, поскольку она не пуста (d.c.).

7.10 Типы переменных и выражения сравнения

В начало страницы

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

1992 POSIX стандарт ввел понятие численной цепочки, что просто означает цепочку, имеющую вид числа, например, " +2". Это понятие используется для определения типа переменной. Тип переменной важен, поскольку типы двух переменных определяют, как их сравнивать.

В gawk определение типа переменной производится по следующим правилам.

1. Численный литерал или результат численной операции имеет численный атрибут.

2. Строковый литерал или результат строковой операции имеет атрибут цепочки.

3. Поля, ввод по getline, FILENAME, элементы ARGV, ENVIRON и элементы массива, созданного с помощью split, которые имеет численные цепочки, имеют атрибут strnum. В противном случае они имеют атрибут цепочки. Неинициализированные переменные также имеют атрибут strnum.

4. Атрибуты передаются через присваивания, но не изменяются при ссылках. Последнее правило особенно важно. В следующей ниже программе a имеет численный тип, несмотря на то что она далее используется в строковой операции.

BEGIN -
a = 12.345 b = a " is a cute number" print b ""

Когда сравниваются два операнда, могут использоваться либо численное, либо строковое сравнения, в зависимости от атрибутов операндов, в соответствии со следующей симметричной матрицей:

        STRING NUMERIC STRNUM
STRING  string string  string
NUMERIC string numeric numeric
STRNUM  string numeric numeric

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

Выражения сравнения сравнивают цепочки или числа для определения отношений, таких как равенство. Они записываются с употреблением операторов отношений, которые представляют супермножество отношений в Cи. Вот их таблица:

x ! y   True если  x меньше чем  y.
x != y  True если  x меньше чем или равен  y.
x ? y   True если  x больше чем  y.
x ?= y  True если  x больше чем или равен  y.
x == y  True если  x равен  y.
x != y  True если  x не равен y.
x ~ y   True если  цепочка x соответствует  regexp обозначенному через  y.
x !~ y  True если  цепочка x не соответствует regexp  y.
subscript в array  True если массив  array имеет элемент с индексом subscript.

Выражение сравнения имеет значение один, если true и ноль, если false. Если сравниваемые операнды имеют разные типы, то численные операнды превращаются в цепочки с использованием значения CONVFMT (см. раздел 7.4 [Преобразования цепочек и чисел], стр. 81).

Цепочки сравниваются сравнением их первых символов, затем вторых, и т.д. Так, "10" меньше "9". Если имеются две цепочки, такие что одна есть начало другой, то короткая меньше длинной. Так, "abc" меньше чем "abcd".

В этом операторе очень легко допустить ошибку, потеряв в `==' одно из `='. Результат останется правильным awk-кодом, но программа будет делать не то, что вы думаете:

if (a = b) # Ошибка! Должно быть  a == b
... else
...

Если b не окажется нулем или пустой строкой, всегда будет работать ветвь if. Из-за похожести операторов эту ошибку очень трудно обнаружить при просмотре входного awk-кода.

Приведем несколько примеров сравнений и их результатов.

1.5 != 2.0       численное сравнение  (true)

"abc" ?= "xyz"   сравнение цепочек  (false)

1.5 != " +2"     сравнение цепочек  (true)

"1e2" ! "3"      сравнение цепочек (true)

a = 2; b = "2" a == b   сравнение цепочек (true)

a = 2; b = " +2" a == b  сравнение цепочек  (false)

В следующем примере

$ echo 1e2 3 -- awk '- print ($1 ! $2) ? "true" : "false" ""'
a false

результат есть `false', так как оба $1 и $2 есть численные цепочки и поэтому оба имеют атрибуты strnum, предписывающие численное сравнение. Цель правил сравнения и использования численных цепочек представляют попытку обеспечить поведение, которое "наименее неожиданно", пока еще "делает правильные вещи."

Сравнения цепочек и сравнения регулярных выражений совершенно различны. Например, x == "foo" имеет значение один, или true, если переменная x есть точно `foo'. Наоборот, x ~ /foo/ имеет значение один если x содержит `foo', например, так: "Oh, what a fool am I!".

Правый операнд в операторах `~' или `!~' может быть или константой regexp (/.../), или обычным выражением, и в этом случае значение выражения как цепочка используется как динамическое regexp (см. раздел 4.1 [Как употреблять регулярные выражения], стр. 23; также см. раздел 4.7 [Использование динамических Regexps], стр. 35).

В последних реализациях awk постоянные регулярные выражения в слешах сами по себе есть также выражения. regexp /regexp/ есть сокращение для выражения сравнения:

$0 ~ /regexp/

Одна специальная позиция, где /foo/ не есть сокращение для `$0 ~ /foo/' есть та, когда это правый операнд в `~' или `!~'! См. раздел 7.2 [употребление констант регулярных выражений], стр. 78, где это обсуждается подробно.

7.11 Булевские выражения

В начало страницы

Булевское выражение есть комбинация выражений сравнения или выражений соответствия с употреблением булевских операторов "или" (`----'), "и" (`&&') и "not" (`!') со скобками для управления порядком действий. Значение истинности булевского выражения вычисляется через значения истинности его компонент. Булевские выражения называются также логическими выражениями. Эти термины эквивалентны.

Булевские выражения могут употребляться всюду, где используются выражения сравнения и соответствия. Они могут использоваться в операторах if, while, do и for (см. главу 9 [Управляющие операторы в действиях], стр. 105). Они имеют численные значения (один если true, ноль если false), которые входят в игру, если значение булевского выражения присваивается переменной или используется в арифметике. Кроме того, каждое булевское выражение есть правильный образец , так что можно использовать единицу как образец при выполнении правил.

Приведем описания трех булевских операторов с примерами.

boolean1 && boolean2

True если  boolean1 и boolean2 есть  true. 

Например, следующий оператор
печатает текущую входную запись, 
если она содержит `2400' и `foo'.

if ($0 ~ /2400/ && $0 ~ /foo/) print

Подвыражение boolean2 вычисляется только если boolean1 есть true. Это может привести к различиям, если boolean2 содержит выражения с побочным эффектом: `$0 ~ /foo/ && ($2 == bar++)'.

Переменная bar не увеличивается, если нет  `foo' в записи.

boolean1 ---- boolean2

True если по крайней мере одна из boolean1 или boolean2 есть true. Например, следующий оператор печатает все записи из ввода, которые содержат или `2400' или `foo', или и то и другое.

if ($0 ~ /2400/ ---- $0 ~ /foo/) print

Подвыражение boolean2 вычисляется только если boolean1 есть false. Это может привести к различиям, когда boolean2 содержит выражения с побочными эффектами.

! boolean       True если boolean есть  false. 

Например, следующая программа печатает 

все записи входного файла `BBS-list' которые не содержат
цепочки  `foo':

awk '- if (! ($0 ~ /foo/)) print ""' BBS-list

Операторы `&&' и `----' называются операторами короткой схемы выполнения (short-circuit operators) из-за вида их работы. Вычисление полного выражения прерывается, если результат может быть определен на полпути их вычисления.

Можно продолжить оператор, который использует `&&' or `----', просто поместив newline после него. Но нельзя помещать newline перед любым из этих операторов, не используя продолжения обратным слешем (см. раздел 2.6 [awk-операторы против строк], стр. 17).

Фактически значение выражения, содержащего оператор `!', будет или единица или ноль, в зависимости от значения, к которому он применен. Оператор `!' часто полезен для изменения смысла переменной-флажка с false на true, и наоборот. Например, следующая программа дает один способ печатать строки между специальными ограничивающими строками:

$1 == "START" - interested = ! interested "" interested == 1 -&
 print "" $1 == "END" - interested = ! interested ""

Переменная interested, подобно всем переменным в awk, в начале инициализируется нулем, который также есть false. Когда появляется строка с первым полем `START', значение ее перекидывается в true, используя `!'. Следующее правило печатает строки, пока interested есть true. Когда появляется строка с первым полем `END', interested переключается опять на false.

7.12 Условные выражения

В начало страницы

Условное выражение есть специальный вид выражения с тремя операндами. Оно позволяет использовать значение первого выражения для выбора одного из двух других. Условное выражение --- такое же как в языке Си:

selector ? if-true-exp : if-false-exp

Имеются три подвыражения. Первое, селектор, всегда вычисляется первым. Если это "true" (не ноль и не пустая цепочка) то затем вычисляется выражение if-true-exp и его значение становится значением всего выражения. В противном случае следующим вычисляется выражение if-false-exp и его значение принимается за значение всего выражения. Например, такое выражение выдает абсолютное значение x:

x ? 0 ? x : -x

Каждый раз когда вычисляется условное выражение, вычисляется одно из выражений if-true-exp и if-false-exp; другое игнорируется. Это важно учитывать, если выражения имеют побочные эффекты. Например, такое условное выражение выдает элемент i либо массива a либо массива b и продвигает i:

x == y ? a[i++] : b[i++]

При этом увеличение i происходит точно один раз, потому что в любом случае вычисляется только одно из увеличивающих его выражений. См. главу 11 [Массивы в awk], стр. 123, для информации о массивах.

В качестве небольшого расширения в gawk допускается продолжение оператора, который использует `?:', простым вставлением newline после любого символа, Однако, нельзя вставлять newline перед любым символом без употребления продолжения обратным слешем (см. раздел 2.6 [Операторы awk против строк], page 17). Если указать `--posix' (см. раздел 14.1 [Параметры командной строки], стр. 161), то это расширение отменяется.

7.13 Вызовы функций

В начало страницы

Функция есть имя некоторой вычислительной процедуры. Так как она имеет имя, ее можно запрашивать именем в любой точке программы. Например, функция sqrt вычисляет квадратный корень из числа. Фиксированное множество функций встроено в awk, что означает их доступность в каждой awk-программе. Функция sqrt есть одна из них. См. главу 12 [Встроенные функции], стр. 135, содержащую список таких функций и их описания. Кроме того, можно определять свои собственные функции в своей программе. См. главу 13 [Функции, определенные пользователем], стр. 153, о том как это делать.

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

sqrt(x^2 + y^2)   один аргумент
atan2(y, x)   два аргумента
rand()        аргументов нет

Не оставляйте пробелов между именем функции и списком аргументов! Имя функции, определенной пользователем, выглядит как имя переменной, и пробел превратит ее в конкатенацию переменной с выражением внутри скобок. Пробел перед скобкой безвреден для встроенных функций, но не надо этого делать, чтобы не создать себе привычки, вредной для пользовательских функций.

Каждая функция ожидает определенное количество аргументов. Например, функция sqrt всегда вызывается с одним аргументом, числом, из которого нужно извлечь квадратный корень: sqrt(argument). Некоторые из встроенных функций позволяют опускать последний аргумент. Если так сделать, они используют что-то по умолчанию. См. главу 12 [Встроенные функции], стр. 135, где есть все детали. Если отсутствуют аргументы в вызовах пользовательских функций, то эти аргументы трактуются как локальные переменные, инициализированные пустыми строками (см. главу 13 [Функции, определенные пользователями], стр. 153).

Подобно другим выражениям, вызовы функций имеют значения, которые вычисляются на основании указанных аргументов. В примере `sqrt(argument)' значение есть квадратный корень из аргумента. Функция может также иметь побочные эффекты, такие как присваивание значений определенным переменным или действия с I/O.

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

$ awk '- print "The square root of", $1, "is", sqrt($1) ""' 1
a The square root of 1 is 1 3
a The square root of 3 is 1.73205 5
a The square root of 5 is 2.23607 Control-d

7.14 Приоритет операторов (Как операторы "гнездятся")

В начало страницы

Приоритет операторов определяет их группировку, когда они входят в состав одного выражения. Например, `*' имеет более высокий приоритет чем `+'; так, `a + b * c' означает перемножить b и c и затем добавить a к произведению (i.e. `a + (b * c)').

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

Когда употребляются вместе операторы одного приоритета, сначала заключается в скобки самый левый оператор, исключая присваивания, условные операторы и возведение в степень, которые группируются в обратном порядке. Так, `a - b + c' группируется как `(a - b) + c', а `a = b = c' группируется как `a = (b = c)'. Приоритет одноместных префиксных операторов не имеет значения до тех пор пока это касается только одноместных операторов, потому что имеется только один путь их интерпретации--сначала внутренние. Так, `$++i' означает `$(++i)' и `++$x' означает `++($x)'. Однако, когда другие операторы следуют за операндом, то приоритет одноместных операторов может играть роль. Так, `$x^2' означает `($x)^2', но `-x^2' есть `-(x^2)', так как `-' имеет меньший приоритет чем `^', а `$' имеет больший приоритет.

Приведем таблицу операторов awk в порядке от наивысшего приоритета к низшему:

(...) Grouping. $ Field. 

++ -- Increment, decrement. 

^ ** Exponentiation.

Эти операторы группируются справа налево. (Оператор  `**' не входит в POSIX.)

+ - ! Одноместные  plus, minus, логическое "not".
* / % Умножение, деление,  modulus.
+ - Сложение, вычитание.

Конкатенация.

Для конкатенации нет специального обозначения. Операнды просто пишутся друг за другом.

! != == != ? ?= ?? --

Отношения и перенаправления.

Операторы отношения и перенаправления имеют одинаковый приоритетный уровень. Символы `?' указывают и отношения и перенаправления; разница между ними определяется по контексту.

Заметим, что операторы перенаправления I/O в операторах print и printf относятся к уровню операторов, а не выражений. Перенаправление не порождает выражений, которые могут быть операндами других операторов. В результате этого не имеет смысла использовать оператор перенаправления около другого оператора с более низким приоритетом, не употребив скобки. Такие комбинации, например, `print foo ? a ? b : c', приводят к синтаксическим ошибкам. Правильный способ написать такой оператор есть `print foo ? (a ? b : c)'.

~ !~ Соответствие, несоответствие.

Компонента массива.

&& Логическое "и".
---- Логическое "или".
?: Условный оператор.

Эти операторы группируются справа налево.

= += -= *= /= %= ^= **=  Присваивания. 

Эти операторы группируются справа
налево. (Оператор `**=' не включен в POSIX.)
В начало страницы

<<< Оглавление Страницы: 7  8 >>>