Выражения

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

Термы и левосторонние списковые операции

Вычисление выражения Perl начинается с выделения в нем так называемых термов и левосторонних списковых операций, вычисленные значения которых «подставляются» в соответствующие места выражения. После этого начинается выполнение разнообразных операций, определяющих вычисляемое выражением значение. Анализ выражения осуществляется слева направо. При этом следует помнить, что и термы, и левосторонние списковые операции имеют одинаковый приоритет.
В Perl термом является любая переменная, любая операция заключения в кавычки, любое выражение в круглых скобках, а также любая функция с параметрами, заключенными в круглые скобки.
Из всех перечисленных объектов языка, которые рассматриваются как термы, требует некоторого пояснения последний — функция с параметрами в круглых скобках.
В действительности в Perl отсутствуют истинные функции, понимаемые в смысле, например, языка С, в котором регламентировано обращение в программе к функции указанием ее имени с фактическими параметрами, заданными в круглых скобках. По существу, «функции» языка Perl являются либо списковыми, либо унарными именованными операциями, которые внешне могут выглядеть как функции, так как синтаксис языка позволяет заключать их параметры в круглые скобки. Вызывая в программе стандартную функцию (с заключенными или не заключенными в скобки параметрами), мы выполняем операцию (списковую или унарную именованную). Причем следует иметь в виду, что коль скоро выполняется операция, то она не только выполняет предписанные ей действия, но и возвращает определенный результат, который используется при вычислении выражения. Например, функция print возвращает «истину», если ее вывод завершен успешно, и «ложь» в противном случае. Что напечатается при вычислении следующей операции?
print print "0";
Ответ — строка 01, так как первая операция print должна напечатать результат вычисления второй операции print, которая, в свою очередь, успешно выводит на экран монитора 0. Следовательно, ее результат «истина», а в Perl она представляется единицей — 1.
Различие между списковой и унарной именованными операциями заключается в их различных отношениях предшествования с последующей запятой. Списковые операции могут получать более одного фактического параметра, а унарные именованные — только один. Поэтому первая встретившаяся после унарной именованной операции запятая завершает ее определение, тогда как в списковых операциях она просто служит разделителем фактических параметров, передаваемых в функцию. Если мы явно не выделяем параметры списковой операции, заключая их в скобки, то все стоящие справа от нее в выражении и разделенные запятыми величины рассматриваются как фактические параметры этой операции. В этом случае говорят, что списковая операция является левосторонней (она расположена слева от своих параметров и других операций, которые могут встретиться справа от нее в выражениях параметров). Левосторонняя списковая операция, как и терм, имеет наивысший приоритет при вычислении выражения. По существу, после определения ее фактических параметров она рассматривается как терм и поэтому вычисляется прежде других операций в выражении.
Например, во фрагменте сценария Perl из листинга 4.3 последний оператор будет выполняться не как печать результатов последовательного выполнения двух операций присваивания (отсортированного массива @n и числа его элементов), а как результат одной операции присваивания массиву @m отсортированного массива (3, 6, 9, 1, 4). 3десь операция sort являстся левосторонней списковой операцией, а поэтому все, что расположено через запятую справа от нее, представляет список ее фактических параметров.
Листинг 4.3. Левосторонние списковые операции
@n = (3,6,9,1);
print @m = sort @n, $n = @n;
В результате выполнения примера из листинга 4.3 переменная $n получит значение 4 (количество элементов массива @n), массив @m будет состоять из чисел 1, 3, 4, 6, 9, а печать будет выглядеть следующим образом:
13469
Если бы мы хотели действительно распечатать отсортированный массив @n, сохранив результат сортировки в массиве @m, и одновременно присвоить переменной $n количество элементов массива @n и распечатать это число, то следовало бы фактические параметры операции sort задать в круглых скобках:
print @n = sort(@n), $n = @n;
Результат выполнения такого оператора выглядел бы следующим образом:
13694

Именованные унарные и правосторонние списковые операции

Как уже отмечалось ранее, в языке Per] определено большое количество встроенных функций, выполняющих разнообразные действия. Однако все они фактически не являются функциями, а просто выглядят как вызовы функций, если их параметры заключены в круглые скобки, — а это допустимо с точки зрения синтаксиса языка. Операции, определяемые стандартными функциями, делятся на два больших класса: списковые и унарные именованные. Списковые операции могут получать несколько фактических параметров в виде списка (отсюда и их название), а унарным именованным для выполнения необходим только один фактический параметр. Некоторые унарные именованные операции из библиотеки стандартных функций перечислены ниже: chdir, cos, defined, goto, log, rand, rmdir, sin, sqrt, do, eval, return
К именованным унарным операциям относятся также все операции проверки файлов и их атрибутов, общий синтаксис которых имеет вид:
-символ [имя_файла дескриптор_файла]
Например, для проверки существования файла определена операция -е, выяснить возможность записи в файл можно с помощью операции -w.
Мы знаем, что списковые операции и унарные именованные операции рассматриваются компилятором Perl как термы, если их фактические параметры заключены в круглые скобки, и тогда они имеют наивысший приоритет выполнения. (Заметим, что заключенный в скобки список фактических параметров может быть отделен от имени соответствующей операции любым числом пробельных символов.)
Если фактический параметр унарной именованной операции не заключен в круглые скобки, то следует принимать во внимание приоритет ее выполнения. Например, так как умножение имеет больший приоритет, чем унарная именованная операция sin, то следующие операции вычисляются так, как указано в комментариях к ним:
use Math::Trig; # В пакете определена константа pi = 3.14159265356979 
sin 1 * pi;     # sin( 1 * pi) = 1.22460635382238e-016
sin (1) * pi;   # (sin 1) * pi = 2.64355906408146
В последнем выражении sin(1) рассматривается как терм, так как после имени операции первой распознаваемой лексемой стоит открывающая круглая скобка, а если это терм, то и вычислять его надо в первую очередь, как операцию с наивысшим приоритетом.
Если в списковой операции отсутствуют скобки вокруг параметров, то, как и в случае с унарной именованной операцией, следует учитывать приоритет ее выполнения. Однако здесь возможны два варианта — списковая операция может иметь либо наивысший, либо самый низкий (ниже только логические операции not, and, or и хоr) приоритет. Это зависит от того, где расположена операция относительно других операций в выражении: слева или справа.
Все операции в выражении, расположенные справа от нее (сама операция расположена в выражении слева от них), имеют более низкий приоритет, так как в этом случае списковая операция является левосторонней с наивысшим, наряду с термом, приоритетом, а расположенные справа от нее операции трактуются как входящие в выражения, представляющие ее фактические параметры.
Любые операции в выражении, расположенные слева от списковой операции (сама операция расположена справа от них), имеют более высокий приоритет относительно такой списковой операции и вычисляются, естественно, раньше нее. Когда в выражении встречается такое расположение списковой операции, то о ней говорят как о правосторонней списковой операции (она расположена справа от остальных операций в выражении). Следующий пример иллюстрирует правостороннее расположение списковой операции:
$m = $n || print "Нуль, пустая строка или не определена!";
По замыслу программиста, это выражение должно напечатать сообщение, если только значение переменной $n равно нулю, пустой строке или не определено, причем значение переменной $m всегда должно равняться значению переменной $n. На первый взгляд кажется, что именно так и должно быть: выполнится операция присвоения переменной $m значения переменной $n. Если оно не равняется нулю, пустой строке или значение переменной $n не определено, то в булевом контексте операции логического ИЛИ (||) оно трактуется как «истина», а поэтому второй операнд этой логической операции (операция печати) не вычисляется, так как она вычисляется по укороченной схеме. Попробуйте оттестировать приведенный оператор с любыми значениями переменной $n. Результат не будет соответствовать замыслу программиста — значение переменной $m будет равно 1, когда переменная $n равна нулю, пустой строке или не определена! Правда, сообщение будет печататься именно тогда, когда переменная $n равна нулю, пустой строке или не определена.
В чем дело? Все дело в приоритете правосторонней списковой операции print ! В выражении эта операция расположена справа от всех остальных операций, поэтому она имеет самый низкий приоритет, и выражение будет вычисляться по следующему алгоритму. Сначала будет вычислен левый операнд операции логического ИЛИ ||, который представлен операцией присваивания $m = $n. Если он имеет значение «истина» (переменная $n определена и имеет значение, не равное нулю или пустой строке), то второй операнд этой операции (списковая операция печати print) вычисляться не будет, а переменной $m будет присвоено значение переменной $n. Если первый операнд вычисляется как «ложь» (переменная $n равна нулю, пустой строке или не определена), то вычисляется второй операнд операции логического ИЛИ, выводящий сообщение на экран монитора. Но так как возвращаемым значением операции печати является «истина» (считаем, что операция выполнилась нормально), то именно она и присваивается переменной $m, а в Perl «истина» представляется числовым значением 1.
Правильное решение — использовать низкоприоритетную операцию логического ИЛИ or:
$m=$n or print "Нуль, пустая строка или не определена!";
или скобками изменить порядок выполнения операций:
($m=$n) || print "Нуль, пустая строка или не определена!";
Завершая разговор о списковых и унарных именованных операциях, следует упомянуть о синтаксической возможности задания первого (или единственного параметра) в круглых скобках так, чтобы он не рассматривался как полный список параметров операции, то есть чтобы операция не выглядела как вызов функции. Для этого следует перед таким параметром поставить префикс +:
sin +(1) * pi; # (sin 1) * pi = 2.64355906408146
Этот префикс не выполняет никакой семантической роли в программе, даже не преобразует параметр в числовой тип данных. Он просто служит для акцентирования того факта, что sin не является функцией, а представляет собой унарную именованную операцию. Аналогично он действует и в случае списковых операций:
sort +($var1+$var2)**2, @m;
Эта операция сортирует возведенную в квадрат сумму значений переменных $var1 и $var2 вместе с элементами массива @m, возвращая массив отсортированных значений. Если убрать префикс + перед выражением в скобках, то результат выполнения операции sort будет возведен в квадрат.

Сочетаемость операций

Завершая разговор о приоритете выполнения операций, следует объяснить свойство сочетаемости операций и его практическое применение. Сочетаемость важна при вычислении выражений, содержащих операции с одинаковым приоритетом, и определяет порядок их вычисления. Рассмотрим выражение:
$m += $n += 1;
Как следует его понимать? Как ($m += $n) += 1 или как $m += ($n += 1)? Ответ дает правило сочетаемости. Все операции присваивания сочетаются справа налево. Это означает, что сначала должно выполниться самое правое присваивание $n += 1, а потом результат увеличенной на единицу переменной $n прибавляется к переменной $m (более левое присваивание). Следовательно, это выражение эквивалентно следующему:
$m +=($n += 1);
Аналогично применяется правило сочетаемости и к другим операциям языка Perl:
$a>$b<$c;   # Эквивалентно: ($a>$b)<$c;
            # Сочетаемость: слева направо.
$a**$b**$c; # Эквивалентно: $а**($b**$с):
            # Сочетаемость: справа налево
Еще раз напомним об использовании скобок при возникновении сомнения в том, как будет вычисляться выражение. Скобки изменяют порядок вычислений, определяемый по правилу приоритетов и сочетаемости. Любое заключенное в скобки подвыражение будет вычисляться с наивысшим приоритетом, так как Perl рассматривает его как терм, имеющий наивысший приоритет.

Следующая страница Содержание главы


Реклама