Викиучебник
ruwikibooks
https://ru.wikibooks.org/wiki/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0
MediaWiki 1.47.0-wmf.18
first-letter
Медиа
Служебная
Обсуждение
Участник
Обсуждение участника
Викиучебник
Обсуждение Викиучебника
Файл
Обсуждение файла
MediaWiki
Обсуждение MediaWiki
Шаблон
Обсуждение шаблона
Справка
Обсуждение справки
Категория
Обсуждение категории
Полка
Обсуждение полки
Импортировано
Обсуждение импортированного
Рецепт
Обсуждение рецепта
Задача
Обсуждение задачи
TimedText
TimedText talk
Модуль
Обсуждение модуля
Event
Event talk
АОН/АУЦ
0
19970
269932
256297
2026-09-02T23:20:19Z
Leksey
3027
Ссылка
269932
wikitext
text/x-wiki
{{АОН страница}}
{{wikipedia|АУЦ}}
{{Важное|Перечень действующих авиационных учебных центров находится на странице - [[АОН/АУЦ/Список|Список АУЦев]]}}
__NOTOC__
'''АУЦ''' это авиационный учебный центр (сокращенно "АУЦ"), в котором производится обучение пилотов (а также прочего авиационного персонала — техников, бортпроводников). За рубежом такие организации называются ATO или DTO (упрощенная форма АТО). В России аналогом примерным DTO можно считать индивидуального инструктора.
В рамках нашего учебника АОН рассматриваются только те АУЦ, где проходит обучение на частного пилота, пилота СВС, пилота планера и пилота свободного аэростата (а также авиатехников).
После завершения обучения в АУЦ, затем сдачи [[АОН/Пилотское_свидетельство/Тестирование|электронных тестов]] в Росавиации или его территориальных отделения - МТУ, выдается [[АОН/Пилотское свидетельство|свидетельство пилота]].
==Документы==
Выпускнику АУЦ нужно не забыть и потом не потерять следующий [[АОН/Пилотское свидетельство/Документы выпускника АУЦ|набор документов]] (который может ему пригодиться в дальнейшем).
==Форма собственности АУЦ==
Почти все АУЦ являются частными. Государственные учебные центры, где происходит обучение на данные виды воздушных судов отсутствуют (исключение может представлять ЦПСАП, Новосибирск, но он своих воздушных судов не имеет и если работает, то на частной технике). Также у двух аэроклубов ДОСААФ имеются АУЦ, но по сути они также частные.
Студенты государственных училищ гражданской авиации еще в процессе обучения и как промежуточное свидетельство получают Частного пилота, но рекомендовать этот вариант для получения свидетельства частного пилота, наверное, неправильно.
== Список АУЦ в России ==
{{Основная статья|АОН/АУЦ/Список}}
Список АУЦ, где готовят пилотов на воздушные суда АОН, а также авиатехников на ВС АОН.
== Требования к поступающим в АУЦ ==
Поступающий на обучение в АУЦ должен иметь среднее профессиональное или высшее образование или находиться в процессе его получения.
* [[АОН/АУЦ/Поступление|Поступление в АУЦ]] - требования к желающим обучаться в АУЦ.
== Образовательная деятельность ==
Подготовка пилотов в России в настоящее время ведется в рамках образовательной деятельности. Это означает, что организация (АУЦ) должна иметь образовательную лицензию. Сама подготовка также должна подчиняться образовательным правилам. Что является причиной того, что к кандидатам на обучение в АУЦ предъявляются дополнительные правила. Эти требования изложены на странице [[АОН/АУЦ/Поступление|Поступление]].
Более подробно о тех рамках и ограничениях, которые накладываются правилам образовательной деятельности (Законом об образовании) рассказано на странице [[АОН/Образование]].
== Организаторам АУЦ ==
* [[АОН/АУЦ/Организация|Создание нового АУЦ]] - о создании АУЦ
== См. также ==
* [[АОН/АУЦ/Выбор|Выбор АУЦ]] - как выбрать АУЦ.
{{Special:PrefixIndex/{{FULLPAGENAME}}/}}
== Ссылки ==
*[http://www.favt.ru/dejatelnost-letnaya-ekspluataciya-aviacionnie-ushebnie-centry/?id=2394 Реестр АУЦ] на сайте Росавиации
{{АОН}}
<!--{{:АОН/Пилотское_свидетельство/Документы_выпускника_АУЦ}}-->
3dk421w8t1qmfg0vz5knhakwwlhn97p
АОН/АУЦ/Организация
0
20629
269938
268090
2026-09-02T23:47:37Z
Leksey
3027
Ссылка
269938
wikitext
text/x-wiki
{{АОН страница}}
Здесь информация непосредственно о создания АУЦ и его деятельности. Рядовым пилотам она не интересна.
Создание АУЦ и его деятельность регламентируется следующими документами:
* [[АОН/ФАП-289|ФАП №289]] - документ описывающий требование к АУЦ.<ref>[http://ivo.garant.ru/#/document/71363792 ФАП 289]</ref>
* [[АОН/ФАП-399|ФАП №399]] - требования по порядку разработки учебных программ и сроках их утверждения.<ref>[http://ivo.garant.ru/#/document/71832920 ФАП 399]</ref>
* Контрольные карты проверки АУЦ.<ref>[[s:Файл:Форма контрольной карты проверки соответствия заявителя (АУЦ).pdf|Контрольные карты проверки АУЦ]] ([https://m.favt.ru/public/letekspl/02/kontr-karta-fap289.pdf с сайта ФАВТ])</ref>
* Административный регламент №173 <ref>[http://ivo.garant.ru/#/document/70431348 Административный регламент №173]</ref>
== Подача программы ==
''К заявлению прилагается программа подготовки в двух экземплярах. В случае внесения изменений в ранее утвержденную программу подготовки к заявлению прилагается программа подготовки с внесенными изменениями и описание внесенных изменений.''
''11. В случае если по результатам рассмотрения заявления и программы подготовки в соответствии с пунктом 10 настоящих правил несоответствия не выявлены, уполномоченный орган в течение 20 рабочих дней с даты поступления заявления рассматривает программу подготовки.''
== Документы АУЦ ==
Основные документы:
* Программа подготовки
* [[АОН/РД|Руководство по деятельности]] (РД)
* Руководство по качеству
* Система управления безопасностью полетов (СУБП)
== Прочее ==
Разные нормативные акты, касающиеся деятельности АУЦ. Выпущены Минтранс, Минобр, Минтруд.
Инструкторы АУЦ:
* Запрет на совмещение летной работы. Особые условия труда и для этой деятельности установлен особый
режим рабочего времени - [http://ivo.garant.ru/#/document/189086 Приказ Минтранса РФ от 21 ноября 2005 г. N 139]
* Необходимость двух инструкторов. Проверяет инструктор не задействованный в обучении. В т.ч. в теоретическом обучении не задействованный или в наземном. Согласно Постановление Правительства Российской Федерации от 6 августа 2013 г. N 670 г.
== АУЦ и Образование ==
Как связано АУЦ и образовательная деятельность описано на странице [[АОН/Образование]]
{{Wikisource|Файл:Форма контрольной карты проверки соответствия заявителя (АУЦ).pdf}}
== См. также ==
*[[АОН/Пилотское свидетельство/Документы выпускника АУЦ]]
*[[АОН/Законодательство]] - общий перечень нормативного регулирования
*[[АОН/АТБ/Организация|Организация организации по ТО]]
*Регулирование законом об Образовании - [[АОН/АУЦ/Организация/Законодательство|Законодательство]]
==Примечания==
{{Примечания}}
{{АОН}}
27jb5w8q1y3ugwoor4ls0kdi43zftfh
АОН/АУЦ/Поступление
0
28113
269933
257203
2026-09-02T23:25:08Z
Leksey
3027
См также
269933
wikitext
text/x-wiki
Казалось бы, что в АУЦ для получения рекреационного непрофессионального свидетельства частного пилота, который летает для себя может поступить любой. Но это не так. Школьники не могут поступить в АУЦ, а также те кто закончил только школу и нигде не учатся (и не учились), тоже не могут.
== Требования к поступающим ==
Требования прописываются в учебной программе, на которую непосредственно и происходит поступление. От того что написано в этом разделе программы и зависит какие будут требования.
== Кто учился только в школе не может поступить в АУЦ ==
Формально подготовка пилотов АОН относится к такому виду образования как ДПО (дополнительное профессиональное образование), что не позволяет молодым людям (и не только) получить пилотское до момента получения диплома о среднем специальном или высшем образовании. Хотя ФАП по пилотским лицензиям (ФАП-147) позволяет получать его в 16 лет. Т.е. имеется очевидное противоречие между образовательными и авиационными законами;
{{Цитата|“... К освоению дополнительных профессиональных программ допускаются: лица, имеющие среднее профессиональное и (или) высшее образование; лица, получающие среднее профессиональное и (или) высшее образование.”<ref>[http://ivo.garant.ru/#/document/70440506/paragraph/12:0 Приказ Министерства образования и науки РФ от 1 июля 2013 г. N 499
"Об утверждении Порядка организации и осуществления образовательной деятельности по дополнительным профессиональным программам" пункт 3]</ref>}}
== Параллельное обучение в АУЦ и в ВУЗ/ССУЗ ==
Формально можно учиться одновременно в АУЦ и получать среднее профессиональное или высшее образование. Закон это позволяет. Но по факту так случается редко.
{{Цитата|При освоении дополнительной профессиональной программы параллельно с получением среднего профессионального образования и (или) высшего образования удостоверение о повышении квалификации и (или) диплом о профессиональной переподготовке выдаются одновременно с получением соответствующего документа об образовании и о квалификации.<ref>[http://ivo.garant.ru/#/document/70291362/paragraph/1016:2 ч.16 ст.76 ФЗ Об образовании]</ref>}}
==Выдача диплома в АУЦ получающему образование ==
{{Цитата|16. При освоении дополнительной профессиональной программы параллельно с получением
среднего профессионального образования и (или) высшего образования удостоверение о повышении
квалификации и (или) диплом о профессиональной переподготовке выдаются одновременно с
получением соответствующего документа об образовании и о квалификации.<ref>[http://ivo.garant.ru/#/document/70291362/paragraph/1015:0 Федеральный закон от 29 декабря 2012 г. N 273-ФЗ "Об образовании" Статья 76 п.16]</ref>}}
== Документы ==
Соответственно при поступлении предоставляют диплом либо либо справку о том, что слушатель является студентом ССУЗ или ВУЗ.
==Сравнение АУЦ и автошколы ==
С автошколами, например, иная история, и в них может поступить любой, даже не имеющий среднего образования, потому что их деятельность отнесена к другому виду подготовки (профессиональной).
== Почему АУЦ это ДПО ==
Базовый вопрос - почему вообще обучение в АУЦ на частного пилота относится к ДПО. Авиационный персонал закон об Образовании разрешает готовить тремя способами.
#В рамках профессионального обучения (не применяется на практике - нет в перечене рабочих специальностей Минтруда специальности "пилот"
#В среднем профессиональном и высшем образовании - это вариант для государственных училищ и вузов гражданской авиации. Такие организации перечислены тут [[Как_стать_пилотом_в_России]].
#В рамках ДПО. Все АУЦ поэтому и работают в этом формате. Перечислены они тут [[АОН/АУЦ]].
{{Цитата|Статья 85. Особенности реализации образовательных программ в области подготовки специалистов авиационного персонала гражданской авиации, <пропущено>...<BR>
1. В области подготовки специалистов авиационного персонала гражданской авиации, <пропущено>, реализуются следующие образовательные программы:<BR>
1) основные программы профессионального обучения;<BR>
2) образовательные программы среднего профессионального образования и образовательные программы высшего образования;<BR>
3) дополнительные профессиональные программы.<ref>[http://ivo.garant.ru/#/document/70291362/paragraph/1139:0 Федеральный закон от 29 декабря 2012 г. N 273-ФЗ "Об образовании" Статья 85.]</ref>}}
== См. также ==
* [[АОН/Образование]]
==Примечания ==
<references />
{{Шаблон:АОН}}
jareqqvnwrt52cd8naa32n7zemv9nw6
Викиучебник:GUS2Wiki
4
30040
269939
269871
2026-09-03T04:43:09Z
Alexis Jazz
57039
Updating gadget usage statistics from [[Special:GadgetUsage]] ([[phab:T121049]])
269939
wikitext
text/x-wiki
{{#ifexist:Project:GUS2Wiki/top|{{/top}}|This page provides a historical record of [[Special:GadgetUsage]] through its page history. To get the data in CSV format, see wikitext. To customize this message or add categories, create [[/top]].}}
{|style="width:100%; color:#606000; background-color: #FFFFE0; border:1px solid #EEEE80; padding:2px; margin-bottom:1em" cellpadding=0
|-
|<imagemap>Image:Clock and warning.svg|20px
rect 100 100 100 100 [[##]]
desc none</imagemap>
| Следующие данные '''взяты из кеша''', последний раз он обновлялся в '''2026-09-01T09:25:03Z'''.
|}
{| class="sortable wikitable"
! Гаджет !! data-sort-type="number" | Количество участников !! data-sort-type="number" | Активные участники
|-
|DotsSyntaxHighlighter || 30 || 2
|-
|HotCat || 80 || 1
|-
|ImageAnnotator || 25 || 1
|-
|contribsrange || 68 || 1
|-
|directLinkToCommons || 30 || 1
|-
|edittop || data-sort-value="Infinity" | По умолчанию || data-sort-value="Infinity" | По умолчанию
|-
|exlinks || 99 || 2
|-
|extWikiLinksMarker || 18 || 3
|-
|histcomb || 156 || 1
|-
|markadmins || 52 || 3
|-
|markblocked || 41 || 2
|-
|popups || 39 || 1
|-
|preview || 119 || 1
|-
|referenceTooltips || data-sort-value="Infinity" | По умолчанию || data-sort-value="Infinity" | По умолчанию
|-
|removeAccessKeys || 26 || 1
|-
|urldecoder || 32 || 1
|}
* [[Служебная:Использование гаджетов]]
* [[m:Meta:GUS2Wiki/Script|GUS2Wiki]]
<!-- data in CSV format:
DotsSyntaxHighlighter,30,2
HotCat,80,1
ImageAnnotator,25,1
contribsrange,68,1
directLinkToCommons,30,1
edittop,default,default
exlinks,99,2
extWikiLinksMarker,18,3
histcomb,156,1
markadmins,52,3
markblocked,41,2
popups,39,1
preview,119,1
referenceTooltips,default,default
removeAccessKeys,26,1
urldecoder,32,1
-->
8phn8qpybjjlp81bd1vcd3fshyu6j3t
Некоторые сведения о Perl 5/Операции и выражения
0
30390
269928
269879
2026-09-02T16:24:54Z
ScriptMaster
15708
/* Булевы выражения */
269928
wikitext
text/x-wiki
{{Навигация учебника}}
''Операция'' — это некоторые действия над операндами, результатом которых является новое значение. В Perl есть десятки операций, большинство из которых мы рассмотрим в этой главе, а другие в последующих. Операции можно делить по типу выполняемых ими действий (например, арифметические, логические и т.д.), по объекту, на который они воздействуют (строковые, списковые, файловые), а иной раз сложно отнести их к какой-то категории.
Большинство операций работает с двумя операндами, которые записываются по обе стороны от знака операции (''бинарная операция''). Например в операции сложения операндами выступают слагаемые. Для других операций может требоваться один операнд (''унарная операция'') или три (''тернарная операция''). В некоторых операциях какие-то операнды можно опускать.
Серия операций, записанная единым куском образует ''выражение''. Любое выражение интерпретируется слева направо, при этом порядок исполнения операций внутри выражения зависит от приоритета операций. Приоритеты операций в выражении можно менять через круглые скобки, которые дают более высокий приоритет.
== Арифметические операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Арифметические операции'''</center>
|-
|<center><code>+</code></center>
|style="width:10%;"|<center><code>6.35+8.31</code></center>
|style="width:30%;"|'''Сложение'''. Выполняет арифметическое сложение
|<center><code>-</code></center>
|style="width:10%;"|<center><code>9.15-5</code></center>
|style="width:30%;"|'''Вычитание'''. Выполняет арифметическое вычитание
|-
|<center><code>*</code></center>
|<center><code>2*2</code></center>
|'''Умножение'''
|<center><code>/</code></center>
|<center><code>15/3</code></center>
|'''Деление'''
|-
|<center><code>**</code></center>
|<center><code>4**2</code></center>
|'''Возведение в степень'''
|<center><code>%</code></center>
|<center><code>10%5</code></center>
|'''Остаток от деления'''
|-
| colspan="6" | <center>'''Унарные арифметические операции'''</center>
|-
|<center><code>+</code></center>
|<center><code>+5</code></center>
|'''Унарный плюс'''
|<center><code>-</code></center>
|<center><code>-6</code></center>
|'''Унарный минус'''. Делает положительное число отрицательным, а отрицательное — положительным
|-
| colspan="6" | <center>'''Инкремент/декремент'''</center>
|-
|<center><code>++</code></center>
|<center><code>$i = 5, $j=$i++;</code></center>
|'''Постфиксный инкремент'''. Увеличивает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 6, потому что инкремент выполняется после присваивания
|<center><code>++</code></center>
|<center><code>$i = 5, $j=++$i;</code></center>
|'''Префиксный инкремент'''. В отличие от постфиксного инкремента, значение в операнде будет увеличено до всех прочих операций над ним. В данном примере в обеих переменных будет 6
|-
|<center><code>--</code></center>
|<center><code>$i = 5, $j=$i--;</code></center>
|'''Постфиксный декремент'''. Уменьшает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 4, потому что декремент выполняется после присваивания
|<center><code>--</code></center>
|<center><code>$i = 5, $j=--$i;</code></center>
|'''Префиксный декремент'''. В отличие от постфиксного декремента, значение в операнде будет уменьшено до всех прочих операций над ним. В данном примере в обеих переменных будет 4
|}
* В основном арифметические операции направлены на работу с числами, однако операндами могут выступать и строки, причем не обязательно, чтобы они конвертировались в числа. В таких случаях интерпретатор сначала попытается выделить число из символов, начиная с первого. Если выделить число не получается, то результатом конвертации будет 0. Так, строка <code>5gh4</code> будет конвертирована в 5, а <code>g130</code> — в 0.
* Если в операции остаток от деления (<code>%</code>) второй операнд является вещественным числом и имеет дробную часть, то он предварительно преобразуется в целое число путем простого отброса дробной части.
* Отрицательное число <u>нельзя</u> возводить не в целую степень. Интерпретатор не будет выдавать никакой ошибки, но при этом результат всегда будет 0.
* Бинарные арифметические операции выполняются в ''скалярном контексте''. Это означает, что интерпретатор будет ожидать в операндах скалярные переменные. Если в операндах будут массивы, то они будут преобразованы к числу элементов в массиве, а в случае хеш-массива — к числу использованных хеш-записей в нем.
* Инкремент и декремент можно применять к строковым данным. В случае инкремента, если строка состоит только из латинских букв, то результатом будет новая строка, в которой последняя буква будет заменена на следующую по порядку в алфавите, причем строчная буква заменяется строчной, а прописная — прописной. Аналогично происходит, если строка завершается цифрой. Для декремента Perl будет пытаться конвертировать строку в число по обычным правилам, описанным выше.
== Строковые операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Строковые операции'''</center>
|-
|<center><code>.</code></center>
|style="width:10%;"|<center><code>"a"."b"</code></center>
|style="width:30%;"|'''Конкатенация строк'''. Объединяет две строки в одну
|<center><code>x</code></center>
|style="width:10%;"|<center><code>"a" x 3</code></center>
|style="width:30%;"|'''Повторитель'''. Повторяет строку в левом операнде N число раз и возвращает из этого строку
|}
* Строковые операции обычно направлены на строковый тип данных, однако можно использовать и числа. В этом случае числа будут преобразованы в строки.
* Оператор конкатенации не вставляет пробелы между строками. Обычно для объединения с пробелами прибегают к функции <code>join</code>.
* Для операции ''повторения'' левый операнд всегда приводится к строковому значению. Правым операндом должно быть целое число, либо строка, которая может быть приведена к целому числу. В случае вещественного числа, дробная часть будет просто отбрасываться.
* Для операции ''повторения'' левым операндом может быть список слов, заключенный в круглые скобки, т.е. эта операция может работать в ''списковом контексте''. В таком случае список будет повторен указанное число раз. Такой прием используется для порождения списка, передаваемого конструктору массива.
* Знак операции повторения следует всегда отделять хотя бы одним пробелом от его операндов, так как в некоторых ситуациях интерпретатор может принять его за часть имени переменной.
* С операцией повторения связана устойчивая идиома, используемая для быстрого заполнения одинаковыми значениями:<source lang="perl">
@arr[1..5] = (1) x 5;
print "@arr\n";
# 1 1 1 1 1
@hash{qw{ apples bananas pears }} = (0) x 3;
print "@{[%hash]}\n";
# apples 0 bananas 0 pears 0
</source>Если в левом операнде операции повторения используется массив, то операция будет выполняться в списковом контексте, в котором она повторит элементы указанное число раз в новом массиве. Если этот массив присвоить срезу массива или хэша (слева от присваивания), то сгенерированные повторением элементы будут присвоены элементам среза. На практике часто используется прием получения хеша из параметров командной строки, переданных сценарию:<source lang="perl">
my %exists;
@exists{@ARGV} = (1) x @ARGV;
# \ \____ В скалярном контексте развернется в число элементов массива @ARGV.
# \___________________ Получается хеш, в котором ключами будут элементы командной строки.
</source>
== Логические операции ==
=== Операции отношения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Числовые отношения'''</center>
|-
|<center><code>></code></center>
|style="width:10%;"|<center><code>2>1</code></center>
|style="width:30%;"|'''Больше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>>=</code></center>
|style="width:10%;"|<center><code>3>=1</code></center>
|style="width:30%;"|'''Больше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><</code></center>
|<center><code>-3<0</code></center>
|'''Меньше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code><=</code></center>
|<center><code>0<=3</code></center>
|'''Меньше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>==</code></center>
|<center><code>3==3</code></center>
|'''Равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>!=</code></center>
|<center><code>1!=2</code></center>
|'''Не равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><=></code></center>
|<center><code>15<=>63</code></center>
|'''Один из операндов больше другого'''. Возвращает -1, если правый операнд больше левого; 1 — правый операнд меньше левого; 0 — операнды равны
|
|
|
|-
| colspan="6" | <center>'''Строковые отношения'''</center>
|-
|<center><code>eq</code></center>
|<center><code>"a" eq "a"</code></center>
|'''Строки лексикографически равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ne</code></center>
|<center><code>"a" ne "b"</code></center>
|'''Строки лексикографически не равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>lt</code></center>
|<center><code>"a" lt "c"</code></center>
|'''Левая строка лексикографически меньше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>le</code></center>
|<center><code>"a" le "c"</code></center>
|'''Левая строка лексикографически меньше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>gt</code></center>
|<center><code>"d" gt "e"</code></center>
|'''Левая строка лексикографически больше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ge</code></center>
|<center><code>"d" ge "e"</code></center>
|'''Левая строка лексикографически больше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>cmp</code></center>
|<center><code>"a" cmp "b"</code></center>
|'''Лексикографически один из операндов не равен другому'''. Возвращает -1, если правый операнд лексикографически больше левого; 1 — правый операнд лексикографически меньше левого; 0 — операнды лексикографически равны
|
|
|
|}
* Обратите внимание, что операции отношения скопированы с командной оболочки Bourne Shell, но там все было наоборот: <code>eq</code>, <code>ne</code>, <code>lt</code>, <code>le</code>, <code>gt</code> и <code>ge</code> применялись к числам, а привычные нам знаки использовались для лексикографического сравнивания. Об этом нужно помнить, когда вы переключаетесь с программирования для командной оболочки на программирование для Perl.
* Лексикографическое сравнивание означает, что сравниваются ASCII кодировки символов. Так символ <code>a</code> лексикографически будет меньше символа <code>b</code>, потому что код последнего (98) больше первого (97). При лексикографическом сравнивании строк учитывается код каждого символа в строке, а также длина строк.
* ИСТИНА для числовых отношений это число 1, а для строковых — строка <code>"1"</code>.
* В общем случае следует быть осторожным при сравнивании вещественных чисел с целыми, так как тут вступает фактор округления чисел. Следует отметить, что вещественные числа вообще нельзя сравнивать на равенство, так как результат практически всегда будет неправильным. В этом случае следует сравнивать разницу двух вещественных чисел с очень малым числом (погрешностью). Можно использовать и другой приём, когда числа преобразуются в строки и сравниваются как строки.
=== Булевы выражения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Булевы операции'''</center>
|-
|<center><code>&&</code></center>
|style="width:10%;"|<center><code>1 && 1</code></center>
|style="width:30%;"|'''Конъюнкция'''
|<center><code><nowiki>||</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>0 || 1</nowiki></code></center>
|style="width:30%;"|'''Дизъюнкция'''
|-
|<center><code>!</code></center>
|<center><code>! 0</code></center>
|'''Логическое отрицание'''
|<center><code>xor</code></center>
|<center><code>1 xor 1</code></center>
|'''Исключающее ИЛИ'''
|-
|<center><code>and</code></center>
|<center><code>1 and 1</code></center>
|'''Конъюнкция'''. ''Синоним''
|<center><code>or</code></center>
|<center><code>0 or 1</code></center>
|'''Дизъюнкция'''. ''Синоним''
|-
|<center><code>not</code></center>
|<center><code>not 1</code></center>
|'''Логическое отрицание'''. ''Синоним''
|<center><code>^^</code></center>
|<center><code>1 ^^ 1</code></center>
|'''Исключающее ИЛИ'''<ref>Знак операции появился с версии интерпретатора 5.40.</ref>
|-
|<center><code>//</code></center>
|<center><code>$a // $b</code></center>
|'''Слияние'''
|
|
|
|}
* Операции <code>or</code>, <code>and</code>, <code>not</code> и <code>xor</code> появились в Perl, начиная с версии 5.001. Отличие их от аналогичных операций в виде символов в том, что они имеют меньший приоритет при вычислении в сложных выражениях. Такой пониженный приоритет иногда требуется в выражениях по типу <code>$num != 0 or print "\$num == 0";</code>
* Для булевых операций ИСТИНОЙ признается любое скалярное значение не равное <code>0</code>, <code>"0"</code> и <code>""</code>, в противном случае это ЛОЖЬ. Значение <code>undef</code> также приравнивается ко ЛЖИ.
* Булевы операции конъюнкции и дизъюнкции вычисляются по укороченной схеме. Это означает, например, что если при конъюнкции ЛОЖЬ в некотором операнде встречается раньше, чем закончилось выражение, вычисление прекращается на этом операнде с общим результатом ЛОЖЬ.
* Хотя булевы операции рассчитаны в основном на то, чтобы использовать их с числами, некоторые из них можно использовать для работы со строками. Полезной здесь оказывается дизъюнкция, которая может быть использована для записи укороченного присваивания значений по умолчанию, что показано в следующем примере: <source lang="perl">
# Эта процедура просто возвращает строку, переданную в аргументе, но если он не передается,
# то она назначит ему значение по умолчанию.
sub procedure {
my ($arg) = @_;
$arg ||= "default_value";
return $arg;
}
# Вызов без аргумента
print "Result: " . procedure . "\n";
# Result: default_value
# Вызов с аргументом
print "Result: " . procedure "some_value" . "\n";
# Result: some_value
</source> Также, в случаях, когда неопределенные значения недопустимы, она может использоваться для выбора из альтернативы: <source lang="perl">
$var = undef;
$var = $var || "default_value";
print "$var\n";
# default_value
# Размер цепочки может быть сколько угодно большим. В данном примере мы использовали
# undef для демонстрации, но представьте на их местах переменные. Значение по умолчанию
# выбирается с разным приоритетом (первое не пустое с левого края).
$value = undef || undef || undef || "default_value";
print "$value\n";
</source>
* Операция слияния (<code>//</code>) работает так: если левый операнд определен (''defined''), то вычисляется выражение, записанное в первом операнде и его результат возвращается как результат всей операции, иначе, если левый операнд не определен, но определен второй операнд, то его результат возвращается как результат всей операции. Если не определены оба операнда, то возвращается <code>undef</code> как результат всей операции. Технически операция похожа на тернарную операцию (см. ниже), но нацелена она на проверку условия ''определен'' / ''не определен''. Операция имеет пониженный приоритет, поэтому в сложных выражениях её обычно следует оставлять в скобках. Вот примеры, когда она может использоваться:<source lang="perl">
# Взять значение в кеше или вызвать процедуру по извлечению значения
# из хранилища, если значение в нем пусто.
$data = $cache{ $key } // get_from_storage($key);
# ----------------------------------------------------------
$x = 10;
$y = undef;
$z = 5;
# Это выражение ...
$result = $x + ($y // $z);
# ... является коротким способом записать такое
unless (defined $y) {
$result = $x + $z;
} else {
$result = $x + $y;
}
# ----------------------------------------------------------
$discounted_price = 60;
$base_price = 100;
$tax_rate = undef;
$price = $discounted_price // $base_price;
# Если налоговая ставка не определена, то она равняется нулю.
$final = $price * (1 - ($tax_rate // 0));
# ----------------------------------------------------------
$default_config = 'default';
# Будет присвоено первое определенное значение.
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# ----------------------------------------------------------
$default_config = undef;
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# Процедура compute_expensive_value будет вызвана только если config_value
# не определена.
$config_value //= compute_expensive_value();
sub compute_expensive_value {
'computed'
}
print "$config_value\n";
</source>
=== Побитовые операции ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Побитовые операции'''</center>
|-
|<center><code><nowiki>&</nowiki></code></center>
|style="width:10%;"|<center><code>3 & 2</code></center>
|style="width:30%;"|'''Побитовое И'''. Выполняет конъюнкцию двух чисел побитово
|<center><code><nowiki>|</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>3 | 2</nowiki></code></center>
|style="width:30%;"|'''Побитовое ИЛИ'''. Выполняет дизъюнкцию двух чисел побитово
|-
|<center><code>^</code></center>
|<center><code>3 ^ 3</code></center>
|'''Побитовое исключающее ИЛИ'''
|<center><code>~</code></center>
|<center><code>~0</code></center>
|'''Побитовое отрицание'''
|-
|<center><code><<</code></center>
|<center><code>1 << 1</code></center>
|'''Побитовый сдвиг влево'''
|<center><code>>></code></center>
|<center><code>2 >> 1</code></center>
|'''Побитовый сдвиг вправо'''
|}
* Побитовые операции работают только с числами, если хотя бы один операнд является числом (соответственно другой будет сконвертирован в число). Если '''оба''' операнда являются строками, то побитово выполняются операции с кодом каждого символа, даже если строки записаны только цифрами. Если при этом строки разной длины, то меньшая из них дополняется символами <code>\000</code> до равенства.
* Все вещественные числа преобразуются к целым.
* Допустимо использовать любой формат представления чисел: десятичный, шестнадцатеричный и восьмеричный. Язык Perl представляет числа с длиной 32 бит для поразрядных операций, независимо от архитектуры компьютера.
== Операции присваивания ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Операции присваивания'''</center>
|-
|<center><code>=</code></center>
|style="width:10%;"|<center><code>$i = 2</code></center>
|style="width:30%;"|'''Простое присваивание'''
|<center><code>**=</code></center>
|style="width:10%;"|<center><code>$i **= 2</code></center>
|style="width:30%;"|'''Возведение в степень с присваиванием'''
|-
|<center><code>+=</code></center>
|<center><code>$i += 2</code></center>
|'''Сложение с присваиванием'''
|<center><code>-=</code></center>
|<center><code>$i -= 2</code></center>
|'''Вычитание с присваиванием'''
|-
|<center><code>.=</code></center>
|<center><code>$i .= 2</code></center>
|'''Конкатенация с присваиванием'''
|<center><code>*=</code></center>
|<center><code>$i *= 2</code></center>
|'''Умножение с присваиванием'''
|-
|<center><code>/=</code></center>
|<center><code>$i /= 2</code></center>
|'''Деление с присваиванием'''
|<center><code>%=</code></center>
|<center><code>$i %= 2</code></center>
|'''Остаток от деления с присваиванием'''
|-
|<center><code>x=</code></center>
|<center><code>$i x= 2</code></center>
|'''Повторение с присваиванием'''
|<center><code>&=</code></center>
|<center><code>$i &= 2</code></center>
|'''Поразрядное И с присваиванием'''
|-
|<center><code><nowiki>|=</nowiki></code></center>
|<center><code><nowiki>$i |= 2</nowiki></code></center>
|'''Поразрядное ИЛИ с присваиванием'''
|<center><code>^=</code></center>
|<center><code>$i ^= 2</code></center>
|'''Поразрядное исключающее ИЛИ с присваиванием'''
|-
|<center><code><<=</code></center>
|<center><code>$i <<= 2</code></center>
|'''Поразрядный сдвиг влево с присваиванием'''
|<center><code>>>=</code></center>
|<center><code>$i >>= 2</code></center>
|'''Поразрядный сдвиг вправо с присваиванием'''
|-
|<center><code>&&=</code></center>
|<center><code>$i &&= 2</code></center>
|'''Конъюнкция с присваиванием'''
|<center><code><nowiki>||=</nowiki></code></center>
|<center><code><nowiki>$i ||= 2</nowiki></code></center>
|'''Дизъюнкция с присваиванием'''
|-
|<center><code>^^=</code></center>
|<center><code>$i ^^= 1</code></center>
|'''Исключающее ИЛИ с присваиванием'''<ref>Поддерживается с версии 5.42.</ref>
|
|
|
|}
* Простое присваивание является бинарной операцией, правый операнд которой является выражением, а левый — определяет участок памяти (в форме переменной), в который запишется результат выражения. Операция присваивания возвращает левый операнд как результат выражения. По этой причине следующая запись будет синтаксически правильной<source lang="perl">
($i = $j) = 5;
# Если развернуть эту конструкцию, то получается
$i = $j;
$i = 5; # Так как выражение в скобках вернет $i
</source>
* Как в языке Си, в Perl есть составное присваивание, совмещающее в себе операцию и присваивание. Операции с присваиванием также возвращают левый операнд, как результат выражения<source lang="perl">
$b = 1;
$a = ($b += 3); # Аналогично $a = $b = 4
$a += ($b += 3); # Аналогично $a = $a + $b + 3
(($a += 2) **= 2 ) -= 1; # Аналогично $a = ($a + 2) ** 2 - 1
</source>Обратим внимание, что в большинстве ситуаций в сложных выражениях ставить скобки для операций присваивания обязательно, потому что, как вы узнаете ниже, они парсятся интерпретатором справа налево.
== Списковые операции ==
''Списковая операция'' — это операция над списком значений. Операции, которым передаются списки и кажущиеся функциями, на самом деле являются списковыми операциями, например <code>print</code>. Это связано с тем, что списковые операции разрешают помещать свои аргументы в скобки. Так, следующие вызовы равнозначны
<source lang="perl">
print $a, "hello", $b;
print ($a, "hello", $b);
</source>
Списковые операции ожидают на входе список скаляров в некоторой форме, а на выходе отдают тот же список, но модифицированный некоторым образом, либо новый список. Так, следующие операции являются списковыми:
* Создание и конкатенация массивов<source lang="perl">
@array1 = (1,2,3);
@array2 = (4,5,6);
@array3 = (@array1, @array2);
</source>
* Встроенные операции, связанные с манипуляцией значением(ми) в массиве<source lang="perl">
@array = (1,2,3);
push @array, 4; # (1, 2, 3, 4)
pop @array; # (1, 2, 3); 4 возвращается как результат
shift @array; # (2, 3); 1 возвращается как результат
unshift @array, 5; # (5, 2, 3)
splice @array, 0, 2; # (3); (5, 2) возвращается как результат
</source>
* Трансформация (новый массив возвращается как результат)<source lang="perl">
@array = qw { a b c d e f };
# Применение предиката к каждому элементу в списке
print "$_\n" foreach map { $_ . $counter++ } @array;
#
# Вызов map в модификаторе foreach возвращает новый массив, каждый элемент которого
# печатается print слева от модификатора. В этом примере процедура предиката к каждому
# элементу исходного массива добавляет порядковый номер, в результате чего получается
#
# a0
# b1
# c2
# d3
# e4
# f5
#
# Сформировать новый массив, элементы которого хранятся в обратном порядке
print "$_\n" foreach reverse @array;
# f
# e
# d
# c
# b
# a
# Сортировка
use List::Util; # Подключаем модуль для перемешивания элементов для дальнейшего примера.
@array = List::Util::shuffle(@array); # Перемешиваем элементы в случайном порядке.
print "@{array}\n"; # Вывод будет отличаться при каждом вызове: "b f d a e c"
@array = sort @array; # Снова в алфавитном порядке.
print "@{array}\n"; # "a b c d e f"
</source>
* Фильтрующие операции (возвращают подмножество исходного массива как результат)<source lang="perl">
@array = qw { a 1 5 d 6 f 8 9 h j k l 25 };
@numbers = grep /\d+/, @array; # Достаем из исходного массива только цифры
print "@{numbers}\n"; # "1 5 6 8 9 25"
@letters = grep /[a-z]+/, @array; # Достаем из исходного массива только буквы
print "@{letters}\n"; # "a d f h j k l"
</source>
* Агрегирующие операции (как правило, возвращают все элементы массивов одним значением)<source lang="perl">
@words = qw { apple grapes banana };
$value = join ", ", @words; # объединяет все элементы и вставляет между ними разделитель.
print "${value}\n"; # "apple, grapes, banana"
</source>
* Разворачивающие операции (наоборот, из одного значения получают список)<source lang="perl">
@numbers = 1..5;
print "@{numbers}\n"; # "1 2 3 4 5"
$fruits = "apple, banana, grapes";
@fruits = split ", ", $fruits;
print "@{fruits}\n"; # "apple banana grapes"
</source>
Подробнее о пользовательских списковых операциях будет в главе, посвященной функциям.
=== Списковые операции all и any ===
Начиная с версии 5.42, в ядро интерпретатора были интегрированы две новые операции, помогающие работать с множествами без использования модулей. До этого эти операции приходилось подключать в виде функций из модуля <code>List::Util</code>. Вероятно, добавление этих операций в ядро было вдохновлено языком Python, в котором они существуют в виде операторов.
<source lang="perl">
any { <блок-предиката> } @list;
all { <блок-предиката> } @list;
</source>
Операции <code>all</code> и <code>any</code> мимикрируют под операцию <code>grep</code> и используются, чтобы вернуть логический ответ по предикату. Чтобы операция <code>all</code> вернула ИСТИНУ, необходимо чтобы её предикат вернул ИСТИНУ для каждого элемента списка. Соответственно, для операции <code>any</code> ИСТИНОЙ считается ситуация, когда предикат возвращает ИСТИНУ по меньшей мере для одного элемента списка.
Появление этих операций, хоть и с опозданием, очень кстати, так как различного рода проверки множеств — частая задача на практике. Вот лишь несколько примеров.
<source lang="perl">
use v5.42; # Директива требует интерпретатор не ниже указанной версии.
# Следующие директивы нужны по крайней мере для версии 5.42, так как на тот момент
# эти возможности были еще экспериментальными. В будущих версиях эти
# директивы не понадобятся.
use feature 'keyword_any', 'keyword_all'; # Включает операции
no warnings 'experimental::keyword_any', 'experimental::keyword_all'; # Отключает предупреждения
# Использование my в этом месте обусловлено тем, что директива use v5.42
# автоматически включает директиву strict (будет рассмотрено позже).
my @numbers = (2, 4, 6, 8, 10);
# Через специальную переменную $_ предикат получает очередной элемент списка.
if (all { $_ % 2 == 0 } @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Сравните этот код с программой, которую мы бы написали для версии интерпретатора меньше 5.42:
<source lang="perl">
use List::Util qw{ all any }; # Импортируем функции из модуля.
@numbers = (2, 4, 6, 8, 10);
# ____ Запятую можно опустить, так как у функции есть прототип. Для any она опущена.
# V
if (all { $_ % 2 == 0 }, @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Обратите внимание на то, что в старых версиях интерпретатора функции <code>all</code> и <code>any</code> всегда нужно импортировать из модуля <code>List::Util</code>. Однако не спешите полностью списывать этот модуль, так как в нём есть множество других полезных функций.
== Операции заключения в кавычки ==
Кавычки (одинарные, двойные и обратные) используются в Perl, чтобы задавать строковые литералы. Кроме этого есть еще операции, которые внешне похожи на операции заключения в кавычки, и перечислены в таблице ниже.
{|class="wikitable" style="width:100%;"
|-
! Операция
! Эквивалент
! Описание
! Операция
! Эквивалент
! Описание
|-
| colspan="6" | <center>'''Операции заключения в кавычки'''</center>
|-
|<center><code>q{}</code></center>
|style="width:10%;"|<center><code><nowiki>''</nowiki></code></center>
|style="width:30%;"|'''Строковый литерал'''. Подстановки невозможны
|<center><code>qq{}</code></center>
|style="width:10%;"|<center><code>""</code></center>
|style="width:30%;"|'''Строковый литерал'''
|-
|<center><code>qx{}</code></center>
|<center><code><nowiki>``</nowiki></code></center>
|'''Команда системы'''
|<center><code>qw{}</code></center>
|<center><code><nowiki>()</nowiki></code></center>
|'''Список слов'''. Подстановки невозможны
|-
|<center><code>m{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Поиск по регулярному выражению'''
|<center><code>qr{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Шаблон регулярного выражения'''
|-
|<center><code>s{}{}</code></center>
|<center><code>s///</code></center>
|'''Подстановка'''
|<center><code>tr{}{}</code></center>
|<center><code><nowiki>y///</nowiki></code></center>
|'''Транслитерация'''. Подстановки невозможны
|}
* Фигурные скобки в данных операциях представляют собой разделитель. Они могут быть в любой момент заменены на другой символ или любую другую пару скобок, например<source lang="perl">
q{hello}
q/hello/
q!hello!
q<\<hello\>> # Если символы разделителя есть в значении, то они должны экранироваться.
</source>Внутри разделителей разрешается любое число пробелов, а также внутри большинства операций разрешены подстановки, кроме некоторых (указанных в таблице отдельно).
* Операция <code>qw{}</code> позволяет вернуть список слов. Чаще всего она используется, чтобы инициализировать массив.<source lang="perl">
@arr = qw{ one two three };
# Это аналогично
@arr = ("one", "two", "three");
# и
$arr[0] = "one";
$arr[1] = "two";
$arr[2] = "three";
</source>Обратите внимание, что внутри <code>qw{}</code> запятые ставить не нужно: разделителями являются пробелы.
* Об остальных операциях будет рассказано далее по ходу изложения.
=== Операция Here-docs ===
В Perl перекочевала Here-docs операция, придуманная для командной оболочки Bourne Shell, которая позволяет инициализировать переменные многострочными строками, либо передавать их на вход некоторой операции, записывая строки прямо в исходном файле программы. Как и в Bourne Shell она записывается так
<source lang="perl">
$str = <<EOF;
строка 1
строка 2
строка 3
EOF
print <<END;
Hello, World!
END
</source>
Здесь после знака <code><<</code> вводится так называемое стоп-слово. Далее записывается многострочная строка. Интерпретатор будет искать стоп-слово, для того чтобы понять где заканчивается эта строка. Стоп-слово может быть любым строковым литералом. В наших примерах стоп-словами были <code>EOF</code> и <code>END</code>.
За стоп-словом обычно следует ставить символ точки с запятой, так как Here-docs является простым оператором, конец которого находится после ввода стоп-слова. Пропуск точки с запятой является очень частой ошибкой. Тем не менее, если оператор Here-docs является единственным или последним в текущем блоке, точку с запятой можно опускать.
По умолчанию многострочная строка записывается так, как будто она находится в двойных кавычках. Таким образом, в ней будут разрешаться все подстановки и управляющие последовательности. Вы можете явно обозначить, как должен поступать интерпретатор, если будете использовать соответствующие кавычки вокруг стоп-слова.
<source lang="perl">
$var = "Hello";
print <<END;
\t$var, World!
END
# Аналогично предыдущему вызову
print <<"END";
\t$var, World!
END
# С одинарными кавычками переменные перестанут подставляться и
# не будут работать управляющие последовательности
print <<'END';
\t$var, World!
END
</source>
Можно также использовать наклонные кавычки, тогда в Here-docs можно писать процедуры командной оболочки и инициализировать переменные результатом работы этих процедур. Каждая строка в таких процедурах это отдельный ввод команды в оболочку. Следует помнить, что результаты могут зависеть от операционной системы, в которой исполняется Perl.
<source lang="perl">
# Выведет каталоги корневой точки монтирования в *nix системе.
print <<`END`;
ls -l /
END
</source>
Если стоп-слово вводится без кавычек, то нельзя ставить пробелы между <code><<</code> и стоп-словом. Если такое случится, то Perl не распознает такого слова и будет захватывать строки до первой пустой строки в исходном файле.
В одном операторе можно ввести больше одного стоп-слова (когда это удобно), если записать их подряд следующим образом.
<source lang="perl">
print <<"END1".<<'END2';
double quotes
END1
single quotes
END2
</source>
По умолчанию, Here-docs сохраняет все лидирующие пробелы в многострочной строке, а также требует, чтобы стоп-слово всегда находилось в первой колонке. Например,
<source lang="perl">
print <<END;
ONE
TWO
THREE
END
</source>
напечатает
<pre>
ONE
TWO
THREE
</pre>
Такое поведение не всегда удобно, особенно, когда Here-docs вложена в некоторый блок: в этом случае в исходном коде нарушается выравнивание, из-за чего его неудобно читать. Сравните
<source lang="perl">
# Операция размещена в блоке, но мы не можем выровнять отступы как нам хочется, а также
# стоп-слово END должно всегда начинаться с первой колонки.
if (1) {
print <<END;
ONE
TWO
THREE
END
}
</source>
С версии 5.26 для Here-docs была добавлена особая форма (<code><<~</code>), которая вычисляет отступ для многострочной строки не от колонки документа с исходным кодом, а от отступа самого стоп слова. Таким образом, с версии 5.26 тот же фрагмент можно записать с сохранением исходных отступов так:
<source lang="perl">
# С версии 5.26
if (1) {
print <<~END;
ONE
TWO
THREE
END
# ^- отступ для многострочной строки вычисляется от этой колонки.
}
</source>
Операция Here-docs может комбинироваться с другими операциями. Для этого удобно пользоваться мнемоническим приемом, в котором нужно представлять, что очередная строка заменяет собой стоп-слово справа от знака <code><<</code>. Вот несколько примеров:
<source lang="perl">
# Каждая строка подается операции uc, которая переводит символы в верхний регистр.
print uc(<<EOF);
one
two
three
EOF
# Результат:
# ONE
# TWO
# THREE
# Удалит лидирующие пробелы для каждой строки.
print <<'EOF' =~ s/^\s+//mgr;
Line one with leading spaces
Line two with leading spaces
EOF
# Результат:
# Line one with leading spaces
# Line two with leading spaces
# При выводе строк будет приписывать к каждой порядковый номер.
sub numbering {
print ++$counter . ": " . $_ . "\n" for split '\n', $_[0];
}
numbering(<<'EOF');
int main(int argc, char** argv)
{
return 0;
}
EOF
# Результат:
# 1: int main(int argc, char** argv)
# 2: {
# 3: return 0;
# 4: }
# Напечатает между строками промежуточную.
print <<'PART1' . "Middle String\n" . <<'PART2';
This is the first part.
PART1
This is the final part.
PART2
# Результат:
# This is the first part.
# Middle String
# This is the final part.
</source>
== Другие операции ==
=== Диапазон ===
Бинарная операция диапазон <code>..</code> по-разному интерпретируется в скалярном и списковом контекстах.
В списковом контексте операция возвращает список из чисел, начиная с левого операнда и до последнего с шагом 1, если оба операнда являются числами, либо строки, которые будут сгенерированы в лексикографическом порядке. Если операндами являются числа, то левый операнд всегда должен быть меньше правого, иначе операция вернет пустой список. Числа также должны быть всегда целыми (если это не так, то дробная часть будет просто отбрасываться).
Если строка-левый операнд в лексикографическом смысле больше правого, то результат может зависеть от версии интерпретатора <code>perl</code>: в старых версиях по умолчанию просто возвращается левый операнд; в версии 5.32 будет возвращен ряд, начиная с большего операнда и до символа <code>z</code>. Кроме того, нельзя использовать символы из других алфавитов, кроме латинского: для других алфавитов всегда возвращается левый операнд как результат.
<source lang="perl">
(1..5); # 1 2 3 4 5
(-3..5); # -3 -2 -1 0 1 2 3 4 5
("a".."d"); # a b c d
("01".."10"); # 01 02 03 04 05 06 07 08 09 10
("a1".."b2"); # a1 a2 a3 ... a9 b1 b2
</source>
В скалярном контексте диапазон возвращает булево значение и эмулирует поведение операции запятая <code>,</code> из редакторов Sed и AWK (например, конструкция <code>1,3p</code>). Алгоритм поведения может показаться сложным при первом прочтении, тем не менее следует познакомиться с ним:
* Диапазон в скалярном контексте всегда находится в одном из двух состояний, которое может измениться при повторном вычислении. Условно мы назовем их ИСТИНА и ЛОЖЬ.
* Диапазон остается в ЛОЖНОМ состоянии до тех пор, пока ЛОЖНЫМ остается значение левого операнда. Как только он становится ИСТИНОЙ, диапазон переключает состояние в ИСТИНУ и остается в нем, пока правый операнд остается ИСТИННЫМ.
* Если диапазон находился в состоянии ИСТИНА и правый операнд стал ЛОЖНЫМ, то диапазон снова переключается в ЛОЖНОЕ состояние и вся предыдущая логика повторяется заново.
* В ЛОЖНОМ состоянии этот диапазон возвращает пустую строку (или ЛОЖЬ). В состоянии ИСТИНА при каждом повторном вычислении возвращается порядковый номер, начиная с 1.
* В момент перехода из ИСТИНЫ в ЛОЖЬ диапазон возвращает строку, в которой к последнему порядковому номеру добавляется <code>"E0"</code>, например <code>2E0</code>. В ИСТИННОМ состоянии <code>E0</code> будет отбрасываться.
Вот собственно и весь алгоритм. Добавление <code>"E0"</code> при смене состояния эмулирует поведение именно утилиты AWK. В Sed добавление этой строки происходит только на следующем вычислении. Чтобы эмулировать именно поведение Sed, нужно использовать три точки <code>...</code>.
Диапазоны в скалярном контексте в Perl обычно используются при чтении файлов.
=== Операция запятая ===
Операция запятая <code>,</code> является бинарной и ведет себя по-разному в скалярном и списковом контекстах:
* в списковом контексте она является разделителем для элементов в списке;
* в скалярном контексте она сначала вычисляет левый операнд, потом правый и возвращает результат правого операнда как общий результат.
<source lang="perl">
# Так как левый операнд присваивания это переменная на массив, то выражение исполняется в списковом контексте.
# В этом примере запятая разделяет элементы списка, который передается конструктору массива.
@arr = (1, 2);
# Теперь выражение выполняется в скалярном контексте. Оба операнда для запятой являются простыми числами, но
# как результат операции, будет возвращена 2, которая будет присвоена скаляру.
$scalar = (1, 2);
</source>
У запятой есть синоним в виде <code>=></code>, который мы ранее использовали для построения хеш-массивов в конструкторе. В основном этот знак повторяет запятую, но требует чтобы левый операнд всегда имел строковый тип и корректно в него конвертировался.
Операция является излюбленным инструментом трюков интерпретации. Например, следующая конструкция по порядку вызовет все процедуры, при этом в первой печати мы попутно захватываем результат вызова процедуры <code>b</code>, пользуясь тем, что операция используется в списковом контексте, в котором мы можем пользоваться индексацией массива, а во втором случае, выполняя все в скалярном контексте, мы получаем результат процедуры <code>c</code>:
<source lang="perl">
sub a { print "1\n"; "a" }
sub b { print "2\n"; "b" }
sub c { print "3\n"; "c" }
print STDOUT ( &a, &b, &c )[1], "\n";
print STDOUT scalar ( &a, &b, &c ), "\n";
</source>
<pre>
1
2
3
b
1
2
3
c
</pre>
=== Тернарная операция ===
В Perl есть тернарная условная операция, которая есть во многих языках программирования. Она записывается точно также как в Си:
<source lang="perl">
<операнд 1> ? <операнд 2> : <операнд 3>
</source>
Операция работает так. Если <code><операнд 1></code> является ИСТИНОЙ, то возвращается <code><операнд 2></code> как результат тернарной операции, иначе возвращается <code><операнд 3></code>. Результат тернарной операции можно использовать как в левосторонних (''lvalue''), так и в правосторонних выражениях (''rvalue''), главное чтобы они подставлялись в правильном семантическом контексте.
<source lang="perl">
$var = ($num % 3) ? ($var1 += 1) : ($var1 += 2);
(($a % 3) ? ($a += 2) : $a) -= 2; # Пример с lvalue выражением
</source>
=== Ссылочные операции ===
Большую часть времени мы ссылаемся на данные в памяти компьютера через переменные. Однако язык Perl, подобно Си, разрешает ссылаться на данные в памяти с помощью ''указателей'' или ''ссылок'' (в Perl эти термины равнозначны), которые хранят адрес на ячейку в памяти как значение. Ссылочная передача удобна, когда одни и те же данные разделяют множество процедур и при этом не нужно привязываться к конкретным именам или типам в тексте программы. Возможно процедура сама должна разобраться, что хранится в памяти по переданной ссылке и принять решение самостоятельно.
Чтобы получить адрес переменной, нужно перед ее именем написать обратный слеш <code>\</code> (это аналогично операции получения адреса <code>&</code> в языке Си).
<source lang="perl">
# Ссылка на скаляр
$var = 5;
$ptr = \$var; # Теперь здесь хранится адрес $var
# Ссылка на массив
@arr = (1,2,3);
$aptr = \@arr;
# Ссылка на хеш-массив
%hash = (name => "Larry", surname => "Wall");
$hptr = \%hash;
</source>
Обратите внимание, что адреса хранятся в скалярной переменной. Если мы распечатаем значения, то получим примерно следующее:
<source lang="">
SCALAR(0x800075938)
ARRAY(0x8000759b0)
HASH(0x800075aa0)
</source>
Обратите внимание, что в ссылке хранится информация о типе объекта, на который она ссылается и шестнадцатеричный адрес в памяти компьютера.
Чтобы выполнить обратную операцию, т.е. получить данные по ссылке, ее нужно ''разыменовать''. В отличие от Си, в Perl разыменовывание привязано к типу данных, на который она ссылается. Для этого нужно перед именем ссылки поставить тип, на который будет вестись разыменовывание.
<source lang="perl">
# Разыменовываем ссылку на хеш-массив, чтобы передать его функции keys, которая извлечет из него ключи
@keys = keys(%$hptr);
print $$ptr, "\n"; # Разыменовываем указатель на скаляр
</source>
В предыдущем примере с хеш-массивом мы сначала разыменовали ссылку на хеш-массив, указав символ <code>%</code> в начале, а только потом работали с массивом как обычно, передав его функции <code>keys</code>. Perl позволяет также обращаться к данным массивов непосредственно из ссылки (когда это удобно) при помощи операции разыменовывания массивов <code>-></code>.
<source lang="perl">
print $aptr->[0], $hptr->{name}, "\n";
</source>
Подробнее ссылки мы рассмотрим в отдельной главе (''см.'' [[{{Root book name}}/Ссылки|Ссылки]]).
=== Операции связывания ===
Регулярные выражения являются визитной карточкой Perl, который снабжен различными операциями для проверки по ним. Сюда входят поиск (<code>m//</code>), подстановка (<code>s///</code>) и замена (<code>tr///</code>). По умолчанию они работают с переменной <code>$_</code>.
Кроме них есть операции связывания <code>=~</code> и <code>!~</code>, которые выражают соответственно ''совпадает с шаблоном'' и ''не совпадает с шаблоном''.
Регулярные выражения являются очень обширной темой в Perl, поэтому они будут рассмотрены в отдельной главе (''см.'' [[{{Root book name}}/Работа со строками и регулярные выражения|Работа со строками и регулярные выражения]]).
==== Умное связывание ====
Начиная с 5.10.1 и до 5.37.10 в Perl поддерживалась операция умного связывания, которая умеет сравнивать переменные, хранящие разные типы данных. Данная бинарная операция обозначается двумя тильдами <code>~~</code> и изначально была введена для поддержки оператора <code>given..when</code> (в котором она используется неявно), о котором немного рассказано в следующей главе (см. [[Некоторые сведения о Perl 5/Операторы#Given..when|Операторы#Given..when]]).
Эта операция является особенной, так как её контекст определяется не на этапе компиляции, а во время исполнения программы. Для этого она определяет какой тип данных хранится в её операндах и, в зависимости от этих типов, выбирает механизм сравнивания, причем фактические типы в операндах не обязательно должны совпадать, что и делает её «умной». Если в понимании выбранного механизма операнды равны, она возвращает 1, иначе пустую строку <code>""</code>.
В Perl 5.37.10 данная операция была отмечена как неудачный эксперимент и окончательно будет вырезана в 5.42. Здесь мы о ней упоминаем, чтобы предостеречь не использовать её, хотя следует отметить сколько релизов ей удалось пережить.
=== Именованные унарные операции ===
В Perl есть большое число встроенных функций с одним аргументом, которые с точки зрения синтаксического анализатора являются ''унарными именованными операциями''<ref>Слово ''именованный'' подчеркивает тот факт, что у операции есть имя в виде обозначающего слова, при этом они не требуют скобок вокруг аргумента, копируя операции с одним символом.</ref>. Такие операции подобны функциям, но не требуют скобок вокруг параметра, как того требуют простые функции. О том, является ли функция такой операцией, можно узнать из документации. К таким операциям относятся например <code>do</code>, <code>eval</code>, <code>return</code>, <code>goto</code>, <code>sin</code>, <code>sqrt</code> и другие, а также все операции проверок файлов типа <code>-e</code> (файл существует), <code>-w</code> (в файл можно писать) и другие.
Ключевая особенность именованных унарных операций в Perl заключается в их приоритете: они имеют более высокий приоритет, чем большинство бинарных операций, но ниже, чем скобки и операция стрелки (<code>-></code>).
По ходу изложения мы затронем многие из этих операций.
=== Операции ввода/вывода ===
Это операции, которые направлены на вывод результатов в файлы или ввод данных в программу из них. Так, унарную именованную операцию <code>print</code> можно рассматривать как операцию вывода.
Чтобы обеспечить ввод данных в программу из файлов, нужно сначала их открыть и получить специальный программный объект, называемый ''дескриптором файла''. Дескриптор файла может быть получен через операцию в виде угловых скобок <code><дескриптор_файла></code>:
<source lang="perl">
open(DFILE, "net.cfg"); # Открываем файл. Идентификатор дескриптора DFILE может быть произвольным
@content = <DFILE>; # Читаем файл в массив
</source>
Если бы мы открыли файл на запись, то мы можем перенаправить запись в него так
<source lang="perl">
print MYFILE "Hello";
</source>
Тема ввода/вывода является очень обширной, поэтому она будет рассмотрена в отдельной главе (''см.'' [[{{Root book name}}/Ввод и вывод|Ввод и вывод]]).
== Выражения ==
Понимание того, как интерпретатор Perl парсит выражения, поможет вам лучше понять как они должны составляться. ''Выражение'' — это несколько операндов, объединенных одной и более операциями. Операнд в свою очередь также может быть выражением, т.е. одно общее выражение это конгломерат подвыражений.
Синтаксический анализатор Perl, во время разбора выражения, пытается выделить в нем элементарные члены — ''термы''<ref>Если делать сравнение с естественным языком, то терм является тем же самым, что и ''слово'' в предложении.</ref>, а потом соединить их знаками операций, причем он выстраивает операции в некотором порядке, учитывая приоритет.
В Perl термами являются литералы, переменные, выражения в скобках, закавыченная строка символов, а также функции с параметрами в скобках. Только терм может быть операндом. Отметим, что в Perl отсутствуют истинные функции: по существу они являются списковыми, унарными именованными операциями, ведущими себя как функции. Вызывая функцию, мы выполняем списковую или унарную операцию, которая возвращает некоторый результат.
<source lang="perl">
# Следующее выражение демонстрирует списковую операцию. В ней сначала
# вычисляется как терм каждый элемент списка, а затем каждый элемент выводится
# на печать списковой операции.
print 2 + 2 * 25 / 4, ' ', 16; # Напечатает '14.5 16'
# Следующее выражение дает аналогичный результат. Избыточные скобки
# порождают массив, передаваемый операции.
print (2 + 2 * 25 / 4, ' ', 16); # Напечатает '14.5 16'
# Теперь попробуем добавить конкатенацию:
print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 16'
# Обратите внимание, что результат конкатенации потерялся (нет символа '$'
# в выводе). Это произошло потому, что левый операнд конкатенации, как терм
# по приоритету вычисляется первым, т.е. print выводит результат своего терма
# в аргументе, а не результат конкатенации.
# Допустимо
print print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 161$'
# Появление единицы объясняется тем, что внутренний print вернул код 1, который
# был преобразован в строку и был объединен конкатенацией
# c '$'. Так как между внутренним и внешним print нет переноса
# строки, получилась такая склеенная строка.
</source>
При синтаксическом разборе выражений и операторов, как термы рассматриваются конструкции <code>do{}</code> и <code>eval{}</code>, вызовы подпрограмм и методов объектов, анонимные конструкторы массивов скаляров и хешей, а также все операции ввода/вывода.
Еще один немаловажный термин это ''контекст операции''. Операции и термы вычисляются в некотором контексте, от которого зависит поведение операции и возвращаемое ей значение. Существует два основных контекста: ''скалярный'' и ''списковый''. В скалярном контексте интерпретатор пытается вывести из терма скаляр, а в списковом контексте — список литералов <code>qw{}</code>. Контекст определяет обычно тип данных левого операнда. Так, если он является скаляром, то операция выполняется в скалярном контексте; если массив скаляров — списковом контексте. Некоторые операции способны распознавать в каком контексте они исполняются и возвращать свой результат в зависимости от него (универсальные операции).
<source lang="perl">
# В списковой операции print перед списком можно указать выходной дескриптор:
print STDOUT (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '16$'
# Потеря 14.5 Объясняется тем, что левый операнд конкатенации
# находится в скалярном контексте, а в нем операция запятая
# возвращает последний операнд как результат.
# Уберем скобки:
print STDOUT 2 + 2 * 25 / 4, ' ', 16 . '$'; # Напечатает '14.5 16$'
# Теперь каждый элемент списка после вычисления
# передается списковой операции, каждый в скалярном контексте.
</source>
Так как в Perl типизация динамическая<ref>Другими словами, все операции в Perl изначально перегружены.</ref>, понятие контекста операции является аналогом операции ''приведения типов данных'', которая есть в типизированных языках программирования. В большинстве ситуаций Perl знает лучше, в каком контексте вызывать операцию, но иногда приходится ему помогать, используя синтаксические конструкции и специальные функции. Умение предсказывать, в каком контексте выполнится операция в выражении, позволяет программисту Perl придумывать подчас очень запутанные (но с другой стороны короткие) конструкции, которые не могут себе позволить другие языки программирования из-за типизации. Мы рассмотрим некоторые случаи ниже.
<source lang="perl">
# В простом случае левый операнд задает контекст операции.
@arr = ("apple", "pear", "grapes", "banana");
# Левый операнд скаляр, а значит присваивание работает в скалярном контексте.
# В скалярном контексте массивы в правом операнде приводятся к их размеру.
$scalar = @arr;
print "$scalar\n";
# Левый операнд список скаляров, а значит присваивание работает в списковом контексте.
# В списковом контексте присваивание будет копировать элементы из массива в правом операнде
# поэлементно в список в левом операнде. При этом список слева не должен превышать размера массива.
( $element1, $element2, $element3 ) = @arr;
print $element1
. " $element2"
. " $element3\n";
# Если слева будет переменная на массив, операция также будет в списковом контексте,
# и правый массив переопределит значение переменной.
@arr1 = (1..4);
@arr1 = @arr;
print "@arr1\n";
#-------------------------------------------------------------------------------------------------------
# Операция print всегда работает в списковом контексте и все элементы списка пытается интерпретировать
# как строки.
# В стандартной библиотеке Perl есть функция localtime, которая в списковом контексте возвращает
# список из 9 элементов, кодирующих текущую дату.
print localtime; print "\n";
# Выведет примерно такое: 3219121301251120
# Однако, localtime умеет работать в скалярном контексте, в котором выводит дату в формате текущей локали,
# но этот контекст не может использоваться по умолчанию, так как компилятор только знает о том, что
# print умеет работать в списковом контексте.
#
# Этот тот случай, когда мы можем подсказать компилятору выполнить localtime в скалярном контексте,
# чтобы напечатать дату в читаемой форме. Для этого нужно воспользоваться функцией scalar, которая
# принуждает интерпретировать выражение в своем аргументе исключительно в скалярном контексте.
print scalar localtime; print "\n";
# В локали LANG_EN будет печататься в формате "Mon Jan 13 12:32:17 2025".
# Можно поступить проще и привести компилятор к скалярному контексту через другую операцию, которая
# этот контекст использует. Например, операция конкатенации строк всегда работает в скалярном контексте.
# Если компилятор увидит выражение, использующее скалярный контекст, он будет использоваться для всех
# операндов операции. Таким образом, такого же результата мы добиваемся, если напишем так:
print localtime . "\n";
# "Mon Jan 13 12:32:17 2025"
#-------------------------------------------------------------------------------------------------------
# Рассмотрим более сложный пример.
# Пусть дана такая строка:
$word = "111example222";
# Вопрос: как посчитать в ней количество символов цифр и букв и совместить это с печатью в одном компактном выражении?
# Для ответа на вопрос "сколько символов данного класса?" здесь следует использовать регулярные выражения.
# Так как о регулярных выражениях мы будем говорить много позже, возьмите на веру, что регулярное выражение для
# поиска цифр выглядит так '/\d/g', а букв так '/[^\d\s]/g'.
#
# Примечание: '/[^\d\s]/g' не учитывает Unicode символы.
#
# Проблема кроется в том, что регулярные выражения умеют работать и в скалярном, и в списковом контекстах.
# Конкретно нам нужен списковый контекст, в котором операция возвращает массив совпадений по шаблону, который
# мы могли бы преобразовать в размер, тем самым ответив на вопрос "сколько?". Но как нам сказать, чтобы операция
# выполнялась в списковом контексте, а не скалярном, ведь специальной функции типа scalar для этого нет?
#
# Единственный способ сделать это, привести компилятор к этому контексту через другую операцию.
# Существует два распространенных приёма провоцировать списковый контекст:
# * Можно использовать операцию присваивания, в которой левый операнд это пустой массив.
# * Можно использовать операцию разыменовывания ссылки, которая будет работать в контексте типа
# разыменовывания.
# Ниже мы в одном выражении используем оба приема для демонстрационных целей
print "Number of letters: "
. ( () = $word =~ /[a-zA-Z]/g ) . "\n"
. "Number of digits: "
. ( @{ [ $word =~ /\d/g ] } ) . "\n"
;
# Number of letters: 7
# Number of digits: 6
</source>
Не расстраивайтесь, если вы ничего из предыдущих примеров не поняли, так как мы сильно забегаем в них вперед. Эти примеры призваны показать, как сильно выражения завязаны на контекст операции.
Скалярный контекст также можно разделить по типу хранимых данных на ''числовой'', ''строковый'' и ''безразличный''. Perl неспособен заранее распознать этот контекст, поэтому он просто подключает механизм неявного приведения данных, когда скаляры в операндах хранят разные типы.
Еще один специальный скалярный контекст — ''булевый''. Так как в Perl нет булева <code>true</code> или <code>false</code>, Perl анализирует строки и числа в булевом контексте, как это было описано выше: <code>0</code>, <code>"0"</code>, <code>"00"</code>, <code>"0.0"</code> и <code>""</code> это условно ЛОЖЬ, а все остальное ИСТИНА.
Еще одним контекстом является void-контекст, который используется, когда в выражении нет побочных эффектов. Так, вы можете спокойно писать в Perl такие бессмысленные конструкции (они выполняются в void-контексте):
<source lang="perl">
(1,2,3);
$n;
"Hello";
</source>
Этот контекст можно обнаружить, если интерпретатор Perl запущен в режиме вывода предупреждающих сообщений.
Еще один контекст это ''подстановочный контекст'', в котором вычисляются операции заключения в кавычки (кроме одинарных кавычек). В этом контексте подставляются переменные и интерпретируются управляющие последовательности.
Ниже для справки представлены приоритеты операций.
{|class="wikitable" style="width:100%;"
|-
! Приоритет
! Операция
! Сочетаемость
|-
|style="width:10%;"|1
|Вычисление термов и левосторонних списковых операций
|style="width:20%;"|Слева направо
|-
|2
|<code><nowiki>-></nowiki></code>
|Слева направо
|-
|3
|<code><nowiki>++</nowiki></code> <code><nowiki>--</nowiki></code>
|Не сочетаются
|-
|4
|<code><nowiki>**</nowiki></code>
|Справа налево
|-
|5
|<code><nowiki>!</nowiki></code> <code><nowiki>~</nowiki></code> <code><nowiki>\</nowiki></code> унарный <code><nowiki>+</nowiki></code> унарный <code><nowiki>-</nowiki></code>
|Справа налево
|-
|6
|<code><nowiki>=~</nowiki></code> <code><nowiki>!=</nowiki></code>
|Слева направо
|-
|7
|<code><nowiki>*</nowiki></code> <code><nowiki>/</nowiki></code> <code><nowiki>%</nowiki></code> <code><nowiki>x</nowiki></code>
|Слева направо
|-
|8
|<code><nowiki>+</nowiki></code> <code><nowiki>-</nowiki></code> <code><nowiki>.</nowiki></code>
|Слева направо
|-
|9
|<code><nowiki><<</nowiki></code> <code><nowiki>>></nowiki></code>
|Слева направо
|-
|10
|Именованные унарные операции
|Не сочетаются
|-
|11
|<code><nowiki><</nowiki></code> <code><nowiki>></nowiki></code> <code><nowiki><=</nowiki></code> <code><nowiki>>=</nowiki></code> <code><nowiki>lt</nowiki></code> <code><nowiki>gt</nowiki></code> <code><nowiki>le</nowiki></code> <code><nowiki>ge</nowiki></code>
|Не сочетаются
|-
|12
|<code><nowiki>==</nowiki></code> <code><nowiki>!=</nowiki></code> <code><nowiki><=></nowiki></code> <code><nowiki>eq</nowiki></code> <code><nowiki>ne</nowiki></code> <code><nowiki>cmp</nowiki></code>
|Не сочетаются
|-
|13
|<code><nowiki>&</nowiki></code>
|Слева направо
|-
|14
|<code><nowiki>|</nowiki></code> <code><nowiki>^</nowiki></code>
|Слева направо
|-
|15
|<code><nowiki>&&</nowiki></code>
|Слева направо
|-
|16
|<code><nowiki>||</nowiki></code>
|Слева направо
|-
|17
|<code><nowiki>..</nowiki></code> <code><nowiki>...</nowiki></code>
|Не сочетаются
|-
|18
|<code><nowiki>?:</nowiki></code>
|Справа налево
|-
|19
|<code><nowiki>=</nowiki></code> <code><nowiki>**=</nowiki></code> <code><nowiki>+=</nowiki></code> <code><nowiki>-=</nowiki></code> <code><nowiki>.=</nowiki></code> <code><nowiki>*=</nowiki></code> <code><nowiki>/=</nowiki></code> <code><nowiki>%=</nowiki></code> <code><nowiki>x=</nowiki></code> <code><nowiki>&=</nowiki></code> <code><nowiki>|=</nowiki></code> <code><nowiki>^=</nowiki></code> <code><nowiki><<=</nowiki></code> <code><nowiki>>>=</nowiki></code> <code><nowiki>&&=</nowiki></code> <code><nowiki>||=</nowiki></code>
|Справа налево
|-
|20
|<code><nowiki>,</nowiki></code> <code><nowiki>=></nowiki></code>
|Слева направо
|-
|21
|Правосторонние списковые операции
|Не сочетаются
|-
|22
|<code><nowiki>not</nowiki></code>
|Справа налево
|-
|23
|<code><nowiki>and</nowiki></code>
|Слева направо
|-
|24
|<code><nowiki>or</nowiki></code> <code><nowiki>xor</nowiki></code>
|Слева направо
|}
* В данной таблице чем выше приоритет операции и чем левее она стоит (в большинстве случаев), тем раньше она выполняется.
* Приоритет списковой операции зависит от ее места в выражении и может изменяться от наивысшего к наименьшему. Все операции в выражении, стоящие слева от списковой, имеют наивысший приоритет по отношению к ней (смотрите 21 приоритет).
* Свойство сочетаемости означает, что операции одного приоритета могут быть в одном выражении, причем порядок их разрешения может быть слева направо или справа налево.
* Скобки позволяют изменить порядок вычисления, определяемому в обычных условиях приоритетом и сочетаемостью. Скобки всегда наделяют выражение в них наивысшим приоритетом.
== Примечания ==
{{примечания}}
opr1nw2z22bj7k3otazqi4b4rxdwm9x
269929
269928
2026-09-02T16:30:11Z
ScriptMaster
15708
/* Операции присваивания */
269929
wikitext
text/x-wiki
{{Навигация учебника}}
''Операция'' — это некоторые действия над операндами, результатом которых является новое значение. В Perl есть десятки операций, большинство из которых мы рассмотрим в этой главе, а другие в последующих. Операции можно делить по типу выполняемых ими действий (например, арифметические, логические и т.д.), по объекту, на который они воздействуют (строковые, списковые, файловые), а иной раз сложно отнести их к какой-то категории.
Большинство операций работает с двумя операндами, которые записываются по обе стороны от знака операции (''бинарная операция''). Например в операции сложения операндами выступают слагаемые. Для других операций может требоваться один операнд (''унарная операция'') или три (''тернарная операция''). В некоторых операциях какие-то операнды можно опускать.
Серия операций, записанная единым куском образует ''выражение''. Любое выражение интерпретируется слева направо, при этом порядок исполнения операций внутри выражения зависит от приоритета операций. Приоритеты операций в выражении можно менять через круглые скобки, которые дают более высокий приоритет.
== Арифметические операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Арифметические операции'''</center>
|-
|<center><code>+</code></center>
|style="width:10%;"|<center><code>6.35+8.31</code></center>
|style="width:30%;"|'''Сложение'''. Выполняет арифметическое сложение
|<center><code>-</code></center>
|style="width:10%;"|<center><code>9.15-5</code></center>
|style="width:30%;"|'''Вычитание'''. Выполняет арифметическое вычитание
|-
|<center><code>*</code></center>
|<center><code>2*2</code></center>
|'''Умножение'''
|<center><code>/</code></center>
|<center><code>15/3</code></center>
|'''Деление'''
|-
|<center><code>**</code></center>
|<center><code>4**2</code></center>
|'''Возведение в степень'''
|<center><code>%</code></center>
|<center><code>10%5</code></center>
|'''Остаток от деления'''
|-
| colspan="6" | <center>'''Унарные арифметические операции'''</center>
|-
|<center><code>+</code></center>
|<center><code>+5</code></center>
|'''Унарный плюс'''
|<center><code>-</code></center>
|<center><code>-6</code></center>
|'''Унарный минус'''. Делает положительное число отрицательным, а отрицательное — положительным
|-
| colspan="6" | <center>'''Инкремент/декремент'''</center>
|-
|<center><code>++</code></center>
|<center><code>$i = 5, $j=$i++;</code></center>
|'''Постфиксный инкремент'''. Увеличивает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 6, потому что инкремент выполняется после присваивания
|<center><code>++</code></center>
|<center><code>$i = 5, $j=++$i;</code></center>
|'''Префиксный инкремент'''. В отличие от постфиксного инкремента, значение в операнде будет увеличено до всех прочих операций над ним. В данном примере в обеих переменных будет 6
|-
|<center><code>--</code></center>
|<center><code>$i = 5, $j=$i--;</code></center>
|'''Постфиксный декремент'''. Уменьшает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 4, потому что декремент выполняется после присваивания
|<center><code>--</code></center>
|<center><code>$i = 5, $j=--$i;</code></center>
|'''Префиксный декремент'''. В отличие от постфиксного декремента, значение в операнде будет уменьшено до всех прочих операций над ним. В данном примере в обеих переменных будет 4
|}
* В основном арифметические операции направлены на работу с числами, однако операндами могут выступать и строки, причем не обязательно, чтобы они конвертировались в числа. В таких случаях интерпретатор сначала попытается выделить число из символов, начиная с первого. Если выделить число не получается, то результатом конвертации будет 0. Так, строка <code>5gh4</code> будет конвертирована в 5, а <code>g130</code> — в 0.
* Если в операции остаток от деления (<code>%</code>) второй операнд является вещественным числом и имеет дробную часть, то он предварительно преобразуется в целое число путем простого отброса дробной части.
* Отрицательное число <u>нельзя</u> возводить не в целую степень. Интерпретатор не будет выдавать никакой ошибки, но при этом результат всегда будет 0.
* Бинарные арифметические операции выполняются в ''скалярном контексте''. Это означает, что интерпретатор будет ожидать в операндах скалярные переменные. Если в операндах будут массивы, то они будут преобразованы к числу элементов в массиве, а в случае хеш-массива — к числу использованных хеш-записей в нем.
* Инкремент и декремент можно применять к строковым данным. В случае инкремента, если строка состоит только из латинских букв, то результатом будет новая строка, в которой последняя буква будет заменена на следующую по порядку в алфавите, причем строчная буква заменяется строчной, а прописная — прописной. Аналогично происходит, если строка завершается цифрой. Для декремента Perl будет пытаться конвертировать строку в число по обычным правилам, описанным выше.
== Строковые операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Строковые операции'''</center>
|-
|<center><code>.</code></center>
|style="width:10%;"|<center><code>"a"."b"</code></center>
|style="width:30%;"|'''Конкатенация строк'''. Объединяет две строки в одну
|<center><code>x</code></center>
|style="width:10%;"|<center><code>"a" x 3</code></center>
|style="width:30%;"|'''Повторитель'''. Повторяет строку в левом операнде N число раз и возвращает из этого строку
|}
* Строковые операции обычно направлены на строковый тип данных, однако можно использовать и числа. В этом случае числа будут преобразованы в строки.
* Оператор конкатенации не вставляет пробелы между строками. Обычно для объединения с пробелами прибегают к функции <code>join</code>.
* Для операции ''повторения'' левый операнд всегда приводится к строковому значению. Правым операндом должно быть целое число, либо строка, которая может быть приведена к целому числу. В случае вещественного числа, дробная часть будет просто отбрасываться.
* Для операции ''повторения'' левым операндом может быть список слов, заключенный в круглые скобки, т.е. эта операция может работать в ''списковом контексте''. В таком случае список будет повторен указанное число раз. Такой прием используется для порождения списка, передаваемого конструктору массива.
* Знак операции повторения следует всегда отделять хотя бы одним пробелом от его операндов, так как в некоторых ситуациях интерпретатор может принять его за часть имени переменной.
* С операцией повторения связана устойчивая идиома, используемая для быстрого заполнения одинаковыми значениями:<source lang="perl">
@arr[1..5] = (1) x 5;
print "@arr\n";
# 1 1 1 1 1
@hash{qw{ apples bananas pears }} = (0) x 3;
print "@{[%hash]}\n";
# apples 0 bananas 0 pears 0
</source>Если в левом операнде операции повторения используется массив, то операция будет выполняться в списковом контексте, в котором она повторит элементы указанное число раз в новом массиве. Если этот массив присвоить срезу массива или хэша (слева от присваивания), то сгенерированные повторением элементы будут присвоены элементам среза. На практике часто используется прием получения хеша из параметров командной строки, переданных сценарию:<source lang="perl">
my %exists;
@exists{@ARGV} = (1) x @ARGV;
# \ \____ В скалярном контексте развернется в число элементов массива @ARGV.
# \___________________ Получается хеш, в котором ключами будут элементы командной строки.
</source>
== Логические операции ==
=== Операции отношения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Числовые отношения'''</center>
|-
|<center><code>></code></center>
|style="width:10%;"|<center><code>2>1</code></center>
|style="width:30%;"|'''Больше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>>=</code></center>
|style="width:10%;"|<center><code>3>=1</code></center>
|style="width:30%;"|'''Больше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><</code></center>
|<center><code>-3<0</code></center>
|'''Меньше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code><=</code></center>
|<center><code>0<=3</code></center>
|'''Меньше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>==</code></center>
|<center><code>3==3</code></center>
|'''Равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>!=</code></center>
|<center><code>1!=2</code></center>
|'''Не равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><=></code></center>
|<center><code>15<=>63</code></center>
|'''Один из операндов больше другого'''. Возвращает -1, если правый операнд больше левого; 1 — правый операнд меньше левого; 0 — операнды равны
|
|
|
|-
| colspan="6" | <center>'''Строковые отношения'''</center>
|-
|<center><code>eq</code></center>
|<center><code>"a" eq "a"</code></center>
|'''Строки лексикографически равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ne</code></center>
|<center><code>"a" ne "b"</code></center>
|'''Строки лексикографически не равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>lt</code></center>
|<center><code>"a" lt "c"</code></center>
|'''Левая строка лексикографически меньше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>le</code></center>
|<center><code>"a" le "c"</code></center>
|'''Левая строка лексикографически меньше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>gt</code></center>
|<center><code>"d" gt "e"</code></center>
|'''Левая строка лексикографически больше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ge</code></center>
|<center><code>"d" ge "e"</code></center>
|'''Левая строка лексикографически больше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>cmp</code></center>
|<center><code>"a" cmp "b"</code></center>
|'''Лексикографически один из операндов не равен другому'''. Возвращает -1, если правый операнд лексикографически больше левого; 1 — правый операнд лексикографически меньше левого; 0 — операнды лексикографически равны
|
|
|
|}
* Обратите внимание, что операции отношения скопированы с командной оболочки Bourne Shell, но там все было наоборот: <code>eq</code>, <code>ne</code>, <code>lt</code>, <code>le</code>, <code>gt</code> и <code>ge</code> применялись к числам, а привычные нам знаки использовались для лексикографического сравнивания. Об этом нужно помнить, когда вы переключаетесь с программирования для командной оболочки на программирование для Perl.
* Лексикографическое сравнивание означает, что сравниваются ASCII кодировки символов. Так символ <code>a</code> лексикографически будет меньше символа <code>b</code>, потому что код последнего (98) больше первого (97). При лексикографическом сравнивании строк учитывается код каждого символа в строке, а также длина строк.
* ИСТИНА для числовых отношений это число 1, а для строковых — строка <code>"1"</code>.
* В общем случае следует быть осторожным при сравнивании вещественных чисел с целыми, так как тут вступает фактор округления чисел. Следует отметить, что вещественные числа вообще нельзя сравнивать на равенство, так как результат практически всегда будет неправильным. В этом случае следует сравнивать разницу двух вещественных чисел с очень малым числом (погрешностью). Можно использовать и другой приём, когда числа преобразуются в строки и сравниваются как строки.
=== Булевы выражения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Булевы операции'''</center>
|-
|<center><code>&&</code></center>
|style="width:10%;"|<center><code>1 && 1</code></center>
|style="width:30%;"|'''Конъюнкция'''
|<center><code><nowiki>||</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>0 || 1</nowiki></code></center>
|style="width:30%;"|'''Дизъюнкция'''
|-
|<center><code>!</code></center>
|<center><code>! 0</code></center>
|'''Логическое отрицание'''
|<center><code>xor</code></center>
|<center><code>1 xor 1</code></center>
|'''Исключающее ИЛИ'''
|-
|<center><code>and</code></center>
|<center><code>1 and 1</code></center>
|'''Конъюнкция'''. ''Синоним''
|<center><code>or</code></center>
|<center><code>0 or 1</code></center>
|'''Дизъюнкция'''. ''Синоним''
|-
|<center><code>not</code></center>
|<center><code>not 1</code></center>
|'''Логическое отрицание'''. ''Синоним''
|<center><code>^^</code></center>
|<center><code>1 ^^ 1</code></center>
|'''Исключающее ИЛИ'''<ref>Знак операции появился с версии интерпретатора 5.40.</ref>
|-
|<center><code>//</code></center>
|<center><code>$a // $b</code></center>
|'''Слияние'''
|
|
|
|}
* Операции <code>or</code>, <code>and</code>, <code>not</code> и <code>xor</code> появились в Perl, начиная с версии 5.001. Отличие их от аналогичных операций в виде символов в том, что они имеют меньший приоритет при вычислении в сложных выражениях. Такой пониженный приоритет иногда требуется в выражениях по типу <code>$num != 0 or print "\$num == 0";</code>
* Для булевых операций ИСТИНОЙ признается любое скалярное значение не равное <code>0</code>, <code>"0"</code> и <code>""</code>, в противном случае это ЛОЖЬ. Значение <code>undef</code> также приравнивается ко ЛЖИ.
* Булевы операции конъюнкции и дизъюнкции вычисляются по укороченной схеме. Это означает, например, что если при конъюнкции ЛОЖЬ в некотором операнде встречается раньше, чем закончилось выражение, вычисление прекращается на этом операнде с общим результатом ЛОЖЬ.
* Хотя булевы операции рассчитаны в основном на то, чтобы использовать их с числами, некоторые из них можно использовать для работы со строками. Полезной здесь оказывается дизъюнкция, которая может быть использована для записи укороченного присваивания значений по умолчанию, что показано в следующем примере: <source lang="perl">
# Эта процедура просто возвращает строку, переданную в аргументе, но если он не передается,
# то она назначит ему значение по умолчанию.
sub procedure {
my ($arg) = @_;
$arg ||= "default_value";
return $arg;
}
# Вызов без аргумента
print "Result: " . procedure . "\n";
# Result: default_value
# Вызов с аргументом
print "Result: " . procedure "some_value" . "\n";
# Result: some_value
</source> Также, в случаях, когда неопределенные значения недопустимы, она может использоваться для выбора из альтернативы: <source lang="perl">
$var = undef;
$var = $var || "default_value";
print "$var\n";
# default_value
# Размер цепочки может быть сколько угодно большим. В данном примере мы использовали
# undef для демонстрации, но представьте на их местах переменные. Значение по умолчанию
# выбирается с разным приоритетом (первое не пустое с левого края).
$value = undef || undef || undef || "default_value";
print "$value\n";
</source>
* Операция слияния (<code>//</code>) работает так: если левый операнд определен (''defined''), то вычисляется выражение, записанное в первом операнде и его результат возвращается как результат всей операции, иначе, если левый операнд не определен, но определен второй операнд, то его результат возвращается как результат всей операции. Если не определены оба операнда, то возвращается <code>undef</code> как результат всей операции. Технически операция похожа на тернарную операцию (см. ниже), но нацелена она на проверку условия ''определен'' / ''не определен''. Операция имеет пониженный приоритет, поэтому в сложных выражениях её обычно следует оставлять в скобках. Вот примеры, когда она может использоваться:<source lang="perl">
# Взять значение в кеше или вызвать процедуру по извлечению значения
# из хранилища, если значение в нем пусто.
$data = $cache{ $key } // get_from_storage($key);
# ----------------------------------------------------------
$x = 10;
$y = undef;
$z = 5;
# Это выражение ...
$result = $x + ($y // $z);
# ... является коротким способом записать такое
unless (defined $y) {
$result = $x + $z;
} else {
$result = $x + $y;
}
# ----------------------------------------------------------
$discounted_price = 60;
$base_price = 100;
$tax_rate = undef;
$price = $discounted_price // $base_price;
# Если налоговая ставка не определена, то она равняется нулю.
$final = $price * (1 - ($tax_rate // 0));
# ----------------------------------------------------------
$default_config = 'default';
# Будет присвоено первое определенное значение.
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# ----------------------------------------------------------
$default_config = undef;
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# Процедура compute_expensive_value будет вызвана только если config_value
# не определена.
$config_value //= compute_expensive_value();
sub compute_expensive_value {
'computed'
}
print "$config_value\n";
</source>
=== Побитовые операции ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Побитовые операции'''</center>
|-
|<center><code><nowiki>&</nowiki></code></center>
|style="width:10%;"|<center><code>3 & 2</code></center>
|style="width:30%;"|'''Побитовое И'''. Выполняет конъюнкцию двух чисел побитово
|<center><code><nowiki>|</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>3 | 2</nowiki></code></center>
|style="width:30%;"|'''Побитовое ИЛИ'''. Выполняет дизъюнкцию двух чисел побитово
|-
|<center><code>^</code></center>
|<center><code>3 ^ 3</code></center>
|'''Побитовое исключающее ИЛИ'''
|<center><code>~</code></center>
|<center><code>~0</code></center>
|'''Побитовое отрицание'''
|-
|<center><code><<</code></center>
|<center><code>1 << 1</code></center>
|'''Побитовый сдвиг влево'''
|<center><code>>></code></center>
|<center><code>2 >> 1</code></center>
|'''Побитовый сдвиг вправо'''
|}
* Побитовые операции работают только с числами, если хотя бы один операнд является числом (соответственно другой будет сконвертирован в число). Если '''оба''' операнда являются строками, то побитово выполняются операции с кодом каждого символа, даже если строки записаны только цифрами. Если при этом строки разной длины, то меньшая из них дополняется символами <code>\000</code> до равенства.
* Все вещественные числа преобразуются к целым.
* Допустимо использовать любой формат представления чисел: десятичный, шестнадцатеричный и восьмеричный. Язык Perl представляет числа с длиной 32 бит для поразрядных операций, независимо от архитектуры компьютера.
== Операции присваивания ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Операции присваивания'''</center>
|-
|<center><code>=</code></center>
|style="width:10%;"|<center><code>$i = 2</code></center>
|style="width:30%;"|'''Простое присваивание'''
|<center><code>**=</code></center>
|style="width:10%;"|<center><code>$i **= 2</code></center>
|style="width:30%;"|'''Возведение в степень с присваиванием'''
|-
|<center><code>+=</code></center>
|<center><code>$i += 2</code></center>
|'''Сложение с присваиванием'''
|<center><code>-=</code></center>
|<center><code>$i -= 2</code></center>
|'''Вычитание с присваиванием'''
|-
|<center><code>.=</code></center>
|<center><code>$i .= 2</code></center>
|'''Конкатенация с присваиванием'''
|<center><code>*=</code></center>
|<center><code>$i *= 2</code></center>
|'''Умножение с присваиванием'''
|-
|<center><code>/=</code></center>
|<center><code>$i /= 2</code></center>
|'''Деление с присваиванием'''
|<center><code>%=</code></center>
|<center><code>$i %= 2</code></center>
|'''Остаток от деления с присваиванием'''
|-
|<center><code>x=</code></center>
|<center><code>$i x= 2</code></center>
|'''Повторение с присваиванием'''
|<center><code>&=</code></center>
|<center><code>$i &= 2</code></center>
|'''Поразрядное И с присваиванием'''
|-
|<center><code><nowiki>|=</nowiki></code></center>
|<center><code><nowiki>$i |= 2</nowiki></code></center>
|'''Поразрядное ИЛИ с присваиванием'''
|<center><code>^=</code></center>
|<center><code>$i ^= 2</code></center>
|'''Поразрядное исключающее ИЛИ с присваиванием'''
|-
|<center><code><<=</code></center>
|<center><code>$i <<= 2</code></center>
|'''Поразрядный сдвиг влево с присваиванием'''
|<center><code>>>=</code></center>
|<center><code>$i >>= 2</code></center>
|'''Поразрядный сдвиг вправо с присваиванием'''
|-
|<center><code>&&=</code></center>
|<center><code>$i &&= 2</code></center>
|'''Конъюнкция с присваиванием'''
|<center><code><nowiki>||=</nowiki></code></center>
|<center><code><nowiki>$i ||= 2</nowiki></code></center>
|'''Дизъюнкция с присваиванием'''
|-
|<center><code>^^=</code></center>
|<center><code>$i ^^= 1</code></center>
|'''Исключающее ИЛИ с присваиванием'''<ref>Поддерживается с версии 5.42.</ref>
|<center><code>//=</code></center>
|<center><code>$config_value //= $default_value</code></center>
|'''Слияние с присваиванием'''
|}
* Простое присваивание является бинарной операцией, правый операнд которой является выражением, а левый — определяет участок памяти (в форме переменной), в который запишется результат выражения. Операция присваивания возвращает левый операнд как результат выражения. По этой причине следующая запись будет синтаксически правильной<source lang="perl">
($i = $j) = 5;
# Если развернуть эту конструкцию, то получается
$i = $j;
$i = 5; # Так как выражение в скобках вернет $i
</source>
* Как в языке Си, в Perl есть составное присваивание, совмещающее в себе операцию и присваивание. Операции с присваиванием также возвращают левый операнд, как результат выражения<source lang="perl">
$b = 1;
$a = ($b += 3); # Аналогично $a = $b = 4
$a += ($b += 3); # Аналогично $a = $a + $b + 3
(($a += 2) **= 2 ) -= 1; # Аналогично $a = ($a + 2) ** 2 - 1
</source>Обратим внимание, что в большинстве ситуаций в сложных выражениях ставить скобки для операций присваивания обязательно, потому что, как вы узнаете ниже, они парсятся интерпретатором справа налево.
== Списковые операции ==
''Списковая операция'' — это операция над списком значений. Операции, которым передаются списки и кажущиеся функциями, на самом деле являются списковыми операциями, например <code>print</code>. Это связано с тем, что списковые операции разрешают помещать свои аргументы в скобки. Так, следующие вызовы равнозначны
<source lang="perl">
print $a, "hello", $b;
print ($a, "hello", $b);
</source>
Списковые операции ожидают на входе список скаляров в некоторой форме, а на выходе отдают тот же список, но модифицированный некоторым образом, либо новый список. Так, следующие операции являются списковыми:
* Создание и конкатенация массивов<source lang="perl">
@array1 = (1,2,3);
@array2 = (4,5,6);
@array3 = (@array1, @array2);
</source>
* Встроенные операции, связанные с манипуляцией значением(ми) в массиве<source lang="perl">
@array = (1,2,3);
push @array, 4; # (1, 2, 3, 4)
pop @array; # (1, 2, 3); 4 возвращается как результат
shift @array; # (2, 3); 1 возвращается как результат
unshift @array, 5; # (5, 2, 3)
splice @array, 0, 2; # (3); (5, 2) возвращается как результат
</source>
* Трансформация (новый массив возвращается как результат)<source lang="perl">
@array = qw { a b c d e f };
# Применение предиката к каждому элементу в списке
print "$_\n" foreach map { $_ . $counter++ } @array;
#
# Вызов map в модификаторе foreach возвращает новый массив, каждый элемент которого
# печатается print слева от модификатора. В этом примере процедура предиката к каждому
# элементу исходного массива добавляет порядковый номер, в результате чего получается
#
# a0
# b1
# c2
# d3
# e4
# f5
#
# Сформировать новый массив, элементы которого хранятся в обратном порядке
print "$_\n" foreach reverse @array;
# f
# e
# d
# c
# b
# a
# Сортировка
use List::Util; # Подключаем модуль для перемешивания элементов для дальнейшего примера.
@array = List::Util::shuffle(@array); # Перемешиваем элементы в случайном порядке.
print "@{array}\n"; # Вывод будет отличаться при каждом вызове: "b f d a e c"
@array = sort @array; # Снова в алфавитном порядке.
print "@{array}\n"; # "a b c d e f"
</source>
* Фильтрующие операции (возвращают подмножество исходного массива как результат)<source lang="perl">
@array = qw { a 1 5 d 6 f 8 9 h j k l 25 };
@numbers = grep /\d+/, @array; # Достаем из исходного массива только цифры
print "@{numbers}\n"; # "1 5 6 8 9 25"
@letters = grep /[a-z]+/, @array; # Достаем из исходного массива только буквы
print "@{letters}\n"; # "a d f h j k l"
</source>
* Агрегирующие операции (как правило, возвращают все элементы массивов одним значением)<source lang="perl">
@words = qw { apple grapes banana };
$value = join ", ", @words; # объединяет все элементы и вставляет между ними разделитель.
print "${value}\n"; # "apple, grapes, banana"
</source>
* Разворачивающие операции (наоборот, из одного значения получают список)<source lang="perl">
@numbers = 1..5;
print "@{numbers}\n"; # "1 2 3 4 5"
$fruits = "apple, banana, grapes";
@fruits = split ", ", $fruits;
print "@{fruits}\n"; # "apple banana grapes"
</source>
Подробнее о пользовательских списковых операциях будет в главе, посвященной функциям.
=== Списковые операции all и any ===
Начиная с версии 5.42, в ядро интерпретатора были интегрированы две новые операции, помогающие работать с множествами без использования модулей. До этого эти операции приходилось подключать в виде функций из модуля <code>List::Util</code>. Вероятно, добавление этих операций в ядро было вдохновлено языком Python, в котором они существуют в виде операторов.
<source lang="perl">
any { <блок-предиката> } @list;
all { <блок-предиката> } @list;
</source>
Операции <code>all</code> и <code>any</code> мимикрируют под операцию <code>grep</code> и используются, чтобы вернуть логический ответ по предикату. Чтобы операция <code>all</code> вернула ИСТИНУ, необходимо чтобы её предикат вернул ИСТИНУ для каждого элемента списка. Соответственно, для операции <code>any</code> ИСТИНОЙ считается ситуация, когда предикат возвращает ИСТИНУ по меньшей мере для одного элемента списка.
Появление этих операций, хоть и с опозданием, очень кстати, так как различного рода проверки множеств — частая задача на практике. Вот лишь несколько примеров.
<source lang="perl">
use v5.42; # Директива требует интерпретатор не ниже указанной версии.
# Следующие директивы нужны по крайней мере для версии 5.42, так как на тот момент
# эти возможности были еще экспериментальными. В будущих версиях эти
# директивы не понадобятся.
use feature 'keyword_any', 'keyword_all'; # Включает операции
no warnings 'experimental::keyword_any', 'experimental::keyword_all'; # Отключает предупреждения
# Использование my в этом месте обусловлено тем, что директива use v5.42
# автоматически включает директиву strict (будет рассмотрено позже).
my @numbers = (2, 4, 6, 8, 10);
# Через специальную переменную $_ предикат получает очередной элемент списка.
if (all { $_ % 2 == 0 } @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Сравните этот код с программой, которую мы бы написали для версии интерпретатора меньше 5.42:
<source lang="perl">
use List::Util qw{ all any }; # Импортируем функции из модуля.
@numbers = (2, 4, 6, 8, 10);
# ____ Запятую можно опустить, так как у функции есть прототип. Для any она опущена.
# V
if (all { $_ % 2 == 0 }, @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Обратите внимание на то, что в старых версиях интерпретатора функции <code>all</code> и <code>any</code> всегда нужно импортировать из модуля <code>List::Util</code>. Однако не спешите полностью списывать этот модуль, так как в нём есть множество других полезных функций.
== Операции заключения в кавычки ==
Кавычки (одинарные, двойные и обратные) используются в Perl, чтобы задавать строковые литералы. Кроме этого есть еще операции, которые внешне похожи на операции заключения в кавычки, и перечислены в таблице ниже.
{|class="wikitable" style="width:100%;"
|-
! Операция
! Эквивалент
! Описание
! Операция
! Эквивалент
! Описание
|-
| colspan="6" | <center>'''Операции заключения в кавычки'''</center>
|-
|<center><code>q{}</code></center>
|style="width:10%;"|<center><code><nowiki>''</nowiki></code></center>
|style="width:30%;"|'''Строковый литерал'''. Подстановки невозможны
|<center><code>qq{}</code></center>
|style="width:10%;"|<center><code>""</code></center>
|style="width:30%;"|'''Строковый литерал'''
|-
|<center><code>qx{}</code></center>
|<center><code><nowiki>``</nowiki></code></center>
|'''Команда системы'''
|<center><code>qw{}</code></center>
|<center><code><nowiki>()</nowiki></code></center>
|'''Список слов'''. Подстановки невозможны
|-
|<center><code>m{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Поиск по регулярному выражению'''
|<center><code>qr{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Шаблон регулярного выражения'''
|-
|<center><code>s{}{}</code></center>
|<center><code>s///</code></center>
|'''Подстановка'''
|<center><code>tr{}{}</code></center>
|<center><code><nowiki>y///</nowiki></code></center>
|'''Транслитерация'''. Подстановки невозможны
|}
* Фигурные скобки в данных операциях представляют собой разделитель. Они могут быть в любой момент заменены на другой символ или любую другую пару скобок, например<source lang="perl">
q{hello}
q/hello/
q!hello!
q<\<hello\>> # Если символы разделителя есть в значении, то они должны экранироваться.
</source>Внутри разделителей разрешается любое число пробелов, а также внутри большинства операций разрешены подстановки, кроме некоторых (указанных в таблице отдельно).
* Операция <code>qw{}</code> позволяет вернуть список слов. Чаще всего она используется, чтобы инициализировать массив.<source lang="perl">
@arr = qw{ one two three };
# Это аналогично
@arr = ("one", "two", "three");
# и
$arr[0] = "one";
$arr[1] = "two";
$arr[2] = "three";
</source>Обратите внимание, что внутри <code>qw{}</code> запятые ставить не нужно: разделителями являются пробелы.
* Об остальных операциях будет рассказано далее по ходу изложения.
=== Операция Here-docs ===
В Perl перекочевала Here-docs операция, придуманная для командной оболочки Bourne Shell, которая позволяет инициализировать переменные многострочными строками, либо передавать их на вход некоторой операции, записывая строки прямо в исходном файле программы. Как и в Bourne Shell она записывается так
<source lang="perl">
$str = <<EOF;
строка 1
строка 2
строка 3
EOF
print <<END;
Hello, World!
END
</source>
Здесь после знака <code><<</code> вводится так называемое стоп-слово. Далее записывается многострочная строка. Интерпретатор будет искать стоп-слово, для того чтобы понять где заканчивается эта строка. Стоп-слово может быть любым строковым литералом. В наших примерах стоп-словами были <code>EOF</code> и <code>END</code>.
За стоп-словом обычно следует ставить символ точки с запятой, так как Here-docs является простым оператором, конец которого находится после ввода стоп-слова. Пропуск точки с запятой является очень частой ошибкой. Тем не менее, если оператор Here-docs является единственным или последним в текущем блоке, точку с запятой можно опускать.
По умолчанию многострочная строка записывается так, как будто она находится в двойных кавычках. Таким образом, в ней будут разрешаться все подстановки и управляющие последовательности. Вы можете явно обозначить, как должен поступать интерпретатор, если будете использовать соответствующие кавычки вокруг стоп-слова.
<source lang="perl">
$var = "Hello";
print <<END;
\t$var, World!
END
# Аналогично предыдущему вызову
print <<"END";
\t$var, World!
END
# С одинарными кавычками переменные перестанут подставляться и
# не будут работать управляющие последовательности
print <<'END';
\t$var, World!
END
</source>
Можно также использовать наклонные кавычки, тогда в Here-docs можно писать процедуры командной оболочки и инициализировать переменные результатом работы этих процедур. Каждая строка в таких процедурах это отдельный ввод команды в оболочку. Следует помнить, что результаты могут зависеть от операционной системы, в которой исполняется Perl.
<source lang="perl">
# Выведет каталоги корневой точки монтирования в *nix системе.
print <<`END`;
ls -l /
END
</source>
Если стоп-слово вводится без кавычек, то нельзя ставить пробелы между <code><<</code> и стоп-словом. Если такое случится, то Perl не распознает такого слова и будет захватывать строки до первой пустой строки в исходном файле.
В одном операторе можно ввести больше одного стоп-слова (когда это удобно), если записать их подряд следующим образом.
<source lang="perl">
print <<"END1".<<'END2';
double quotes
END1
single quotes
END2
</source>
По умолчанию, Here-docs сохраняет все лидирующие пробелы в многострочной строке, а также требует, чтобы стоп-слово всегда находилось в первой колонке. Например,
<source lang="perl">
print <<END;
ONE
TWO
THREE
END
</source>
напечатает
<pre>
ONE
TWO
THREE
</pre>
Такое поведение не всегда удобно, особенно, когда Here-docs вложена в некоторый блок: в этом случае в исходном коде нарушается выравнивание, из-за чего его неудобно читать. Сравните
<source lang="perl">
# Операция размещена в блоке, но мы не можем выровнять отступы как нам хочется, а также
# стоп-слово END должно всегда начинаться с первой колонки.
if (1) {
print <<END;
ONE
TWO
THREE
END
}
</source>
С версии 5.26 для Here-docs была добавлена особая форма (<code><<~</code>), которая вычисляет отступ для многострочной строки не от колонки документа с исходным кодом, а от отступа самого стоп слова. Таким образом, с версии 5.26 тот же фрагмент можно записать с сохранением исходных отступов так:
<source lang="perl">
# С версии 5.26
if (1) {
print <<~END;
ONE
TWO
THREE
END
# ^- отступ для многострочной строки вычисляется от этой колонки.
}
</source>
Операция Here-docs может комбинироваться с другими операциями. Для этого удобно пользоваться мнемоническим приемом, в котором нужно представлять, что очередная строка заменяет собой стоп-слово справа от знака <code><<</code>. Вот несколько примеров:
<source lang="perl">
# Каждая строка подается операции uc, которая переводит символы в верхний регистр.
print uc(<<EOF);
one
two
three
EOF
# Результат:
# ONE
# TWO
# THREE
# Удалит лидирующие пробелы для каждой строки.
print <<'EOF' =~ s/^\s+//mgr;
Line one with leading spaces
Line two with leading spaces
EOF
# Результат:
# Line one with leading spaces
# Line two with leading spaces
# При выводе строк будет приписывать к каждой порядковый номер.
sub numbering {
print ++$counter . ": " . $_ . "\n" for split '\n', $_[0];
}
numbering(<<'EOF');
int main(int argc, char** argv)
{
return 0;
}
EOF
# Результат:
# 1: int main(int argc, char** argv)
# 2: {
# 3: return 0;
# 4: }
# Напечатает между строками промежуточную.
print <<'PART1' . "Middle String\n" . <<'PART2';
This is the first part.
PART1
This is the final part.
PART2
# Результат:
# This is the first part.
# Middle String
# This is the final part.
</source>
== Другие операции ==
=== Диапазон ===
Бинарная операция диапазон <code>..</code> по-разному интерпретируется в скалярном и списковом контекстах.
В списковом контексте операция возвращает список из чисел, начиная с левого операнда и до последнего с шагом 1, если оба операнда являются числами, либо строки, которые будут сгенерированы в лексикографическом порядке. Если операндами являются числа, то левый операнд всегда должен быть меньше правого, иначе операция вернет пустой список. Числа также должны быть всегда целыми (если это не так, то дробная часть будет просто отбрасываться).
Если строка-левый операнд в лексикографическом смысле больше правого, то результат может зависеть от версии интерпретатора <code>perl</code>: в старых версиях по умолчанию просто возвращается левый операнд; в версии 5.32 будет возвращен ряд, начиная с большего операнда и до символа <code>z</code>. Кроме того, нельзя использовать символы из других алфавитов, кроме латинского: для других алфавитов всегда возвращается левый операнд как результат.
<source lang="perl">
(1..5); # 1 2 3 4 5
(-3..5); # -3 -2 -1 0 1 2 3 4 5
("a".."d"); # a b c d
("01".."10"); # 01 02 03 04 05 06 07 08 09 10
("a1".."b2"); # a1 a2 a3 ... a9 b1 b2
</source>
В скалярном контексте диапазон возвращает булево значение и эмулирует поведение операции запятая <code>,</code> из редакторов Sed и AWK (например, конструкция <code>1,3p</code>). Алгоритм поведения может показаться сложным при первом прочтении, тем не менее следует познакомиться с ним:
* Диапазон в скалярном контексте всегда находится в одном из двух состояний, которое может измениться при повторном вычислении. Условно мы назовем их ИСТИНА и ЛОЖЬ.
* Диапазон остается в ЛОЖНОМ состоянии до тех пор, пока ЛОЖНЫМ остается значение левого операнда. Как только он становится ИСТИНОЙ, диапазон переключает состояние в ИСТИНУ и остается в нем, пока правый операнд остается ИСТИННЫМ.
* Если диапазон находился в состоянии ИСТИНА и правый операнд стал ЛОЖНЫМ, то диапазон снова переключается в ЛОЖНОЕ состояние и вся предыдущая логика повторяется заново.
* В ЛОЖНОМ состоянии этот диапазон возвращает пустую строку (или ЛОЖЬ). В состоянии ИСТИНА при каждом повторном вычислении возвращается порядковый номер, начиная с 1.
* В момент перехода из ИСТИНЫ в ЛОЖЬ диапазон возвращает строку, в которой к последнему порядковому номеру добавляется <code>"E0"</code>, например <code>2E0</code>. В ИСТИННОМ состоянии <code>E0</code> будет отбрасываться.
Вот собственно и весь алгоритм. Добавление <code>"E0"</code> при смене состояния эмулирует поведение именно утилиты AWK. В Sed добавление этой строки происходит только на следующем вычислении. Чтобы эмулировать именно поведение Sed, нужно использовать три точки <code>...</code>.
Диапазоны в скалярном контексте в Perl обычно используются при чтении файлов.
=== Операция запятая ===
Операция запятая <code>,</code> является бинарной и ведет себя по-разному в скалярном и списковом контекстах:
* в списковом контексте она является разделителем для элементов в списке;
* в скалярном контексте она сначала вычисляет левый операнд, потом правый и возвращает результат правого операнда как общий результат.
<source lang="perl">
# Так как левый операнд присваивания это переменная на массив, то выражение исполняется в списковом контексте.
# В этом примере запятая разделяет элементы списка, который передается конструктору массива.
@arr = (1, 2);
# Теперь выражение выполняется в скалярном контексте. Оба операнда для запятой являются простыми числами, но
# как результат операции, будет возвращена 2, которая будет присвоена скаляру.
$scalar = (1, 2);
</source>
У запятой есть синоним в виде <code>=></code>, который мы ранее использовали для построения хеш-массивов в конструкторе. В основном этот знак повторяет запятую, но требует чтобы левый операнд всегда имел строковый тип и корректно в него конвертировался.
Операция является излюбленным инструментом трюков интерпретации. Например, следующая конструкция по порядку вызовет все процедуры, при этом в первой печати мы попутно захватываем результат вызова процедуры <code>b</code>, пользуясь тем, что операция используется в списковом контексте, в котором мы можем пользоваться индексацией массива, а во втором случае, выполняя все в скалярном контексте, мы получаем результат процедуры <code>c</code>:
<source lang="perl">
sub a { print "1\n"; "a" }
sub b { print "2\n"; "b" }
sub c { print "3\n"; "c" }
print STDOUT ( &a, &b, &c )[1], "\n";
print STDOUT scalar ( &a, &b, &c ), "\n";
</source>
<pre>
1
2
3
b
1
2
3
c
</pre>
=== Тернарная операция ===
В Perl есть тернарная условная операция, которая есть во многих языках программирования. Она записывается точно также как в Си:
<source lang="perl">
<операнд 1> ? <операнд 2> : <операнд 3>
</source>
Операция работает так. Если <code><операнд 1></code> является ИСТИНОЙ, то возвращается <code><операнд 2></code> как результат тернарной операции, иначе возвращается <code><операнд 3></code>. Результат тернарной операции можно использовать как в левосторонних (''lvalue''), так и в правосторонних выражениях (''rvalue''), главное чтобы они подставлялись в правильном семантическом контексте.
<source lang="perl">
$var = ($num % 3) ? ($var1 += 1) : ($var1 += 2);
(($a % 3) ? ($a += 2) : $a) -= 2; # Пример с lvalue выражением
</source>
=== Ссылочные операции ===
Большую часть времени мы ссылаемся на данные в памяти компьютера через переменные. Однако язык Perl, подобно Си, разрешает ссылаться на данные в памяти с помощью ''указателей'' или ''ссылок'' (в Perl эти термины равнозначны), которые хранят адрес на ячейку в памяти как значение. Ссылочная передача удобна, когда одни и те же данные разделяют множество процедур и при этом не нужно привязываться к конкретным именам или типам в тексте программы. Возможно процедура сама должна разобраться, что хранится в памяти по переданной ссылке и принять решение самостоятельно.
Чтобы получить адрес переменной, нужно перед ее именем написать обратный слеш <code>\</code> (это аналогично операции получения адреса <code>&</code> в языке Си).
<source lang="perl">
# Ссылка на скаляр
$var = 5;
$ptr = \$var; # Теперь здесь хранится адрес $var
# Ссылка на массив
@arr = (1,2,3);
$aptr = \@arr;
# Ссылка на хеш-массив
%hash = (name => "Larry", surname => "Wall");
$hptr = \%hash;
</source>
Обратите внимание, что адреса хранятся в скалярной переменной. Если мы распечатаем значения, то получим примерно следующее:
<source lang="">
SCALAR(0x800075938)
ARRAY(0x8000759b0)
HASH(0x800075aa0)
</source>
Обратите внимание, что в ссылке хранится информация о типе объекта, на который она ссылается и шестнадцатеричный адрес в памяти компьютера.
Чтобы выполнить обратную операцию, т.е. получить данные по ссылке, ее нужно ''разыменовать''. В отличие от Си, в Perl разыменовывание привязано к типу данных, на который она ссылается. Для этого нужно перед именем ссылки поставить тип, на который будет вестись разыменовывание.
<source lang="perl">
# Разыменовываем ссылку на хеш-массив, чтобы передать его функции keys, которая извлечет из него ключи
@keys = keys(%$hptr);
print $$ptr, "\n"; # Разыменовываем указатель на скаляр
</source>
В предыдущем примере с хеш-массивом мы сначала разыменовали ссылку на хеш-массив, указав символ <code>%</code> в начале, а только потом работали с массивом как обычно, передав его функции <code>keys</code>. Perl позволяет также обращаться к данным массивов непосредственно из ссылки (когда это удобно) при помощи операции разыменовывания массивов <code>-></code>.
<source lang="perl">
print $aptr->[0], $hptr->{name}, "\n";
</source>
Подробнее ссылки мы рассмотрим в отдельной главе (''см.'' [[{{Root book name}}/Ссылки|Ссылки]]).
=== Операции связывания ===
Регулярные выражения являются визитной карточкой Perl, который снабжен различными операциями для проверки по ним. Сюда входят поиск (<code>m//</code>), подстановка (<code>s///</code>) и замена (<code>tr///</code>). По умолчанию они работают с переменной <code>$_</code>.
Кроме них есть операции связывания <code>=~</code> и <code>!~</code>, которые выражают соответственно ''совпадает с шаблоном'' и ''не совпадает с шаблоном''.
Регулярные выражения являются очень обширной темой в Perl, поэтому они будут рассмотрены в отдельной главе (''см.'' [[{{Root book name}}/Работа со строками и регулярные выражения|Работа со строками и регулярные выражения]]).
==== Умное связывание ====
Начиная с 5.10.1 и до 5.37.10 в Perl поддерживалась операция умного связывания, которая умеет сравнивать переменные, хранящие разные типы данных. Данная бинарная операция обозначается двумя тильдами <code>~~</code> и изначально была введена для поддержки оператора <code>given..when</code> (в котором она используется неявно), о котором немного рассказано в следующей главе (см. [[Некоторые сведения о Perl 5/Операторы#Given..when|Операторы#Given..when]]).
Эта операция является особенной, так как её контекст определяется не на этапе компиляции, а во время исполнения программы. Для этого она определяет какой тип данных хранится в её операндах и, в зависимости от этих типов, выбирает механизм сравнивания, причем фактические типы в операндах не обязательно должны совпадать, что и делает её «умной». Если в понимании выбранного механизма операнды равны, она возвращает 1, иначе пустую строку <code>""</code>.
В Perl 5.37.10 данная операция была отмечена как неудачный эксперимент и окончательно будет вырезана в 5.42. Здесь мы о ней упоминаем, чтобы предостеречь не использовать её, хотя следует отметить сколько релизов ей удалось пережить.
=== Именованные унарные операции ===
В Perl есть большое число встроенных функций с одним аргументом, которые с точки зрения синтаксического анализатора являются ''унарными именованными операциями''<ref>Слово ''именованный'' подчеркивает тот факт, что у операции есть имя в виде обозначающего слова, при этом они не требуют скобок вокруг аргумента, копируя операции с одним символом.</ref>. Такие операции подобны функциям, но не требуют скобок вокруг параметра, как того требуют простые функции. О том, является ли функция такой операцией, можно узнать из документации. К таким операциям относятся например <code>do</code>, <code>eval</code>, <code>return</code>, <code>goto</code>, <code>sin</code>, <code>sqrt</code> и другие, а также все операции проверок файлов типа <code>-e</code> (файл существует), <code>-w</code> (в файл можно писать) и другие.
Ключевая особенность именованных унарных операций в Perl заключается в их приоритете: они имеют более высокий приоритет, чем большинство бинарных операций, но ниже, чем скобки и операция стрелки (<code>-></code>).
По ходу изложения мы затронем многие из этих операций.
=== Операции ввода/вывода ===
Это операции, которые направлены на вывод результатов в файлы или ввод данных в программу из них. Так, унарную именованную операцию <code>print</code> можно рассматривать как операцию вывода.
Чтобы обеспечить ввод данных в программу из файлов, нужно сначала их открыть и получить специальный программный объект, называемый ''дескриптором файла''. Дескриптор файла может быть получен через операцию в виде угловых скобок <code><дескриптор_файла></code>:
<source lang="perl">
open(DFILE, "net.cfg"); # Открываем файл. Идентификатор дескриптора DFILE может быть произвольным
@content = <DFILE>; # Читаем файл в массив
</source>
Если бы мы открыли файл на запись, то мы можем перенаправить запись в него так
<source lang="perl">
print MYFILE "Hello";
</source>
Тема ввода/вывода является очень обширной, поэтому она будет рассмотрена в отдельной главе (''см.'' [[{{Root book name}}/Ввод и вывод|Ввод и вывод]]).
== Выражения ==
Понимание того, как интерпретатор Perl парсит выражения, поможет вам лучше понять как они должны составляться. ''Выражение'' — это несколько операндов, объединенных одной и более операциями. Операнд в свою очередь также может быть выражением, т.е. одно общее выражение это конгломерат подвыражений.
Синтаксический анализатор Perl, во время разбора выражения, пытается выделить в нем элементарные члены — ''термы''<ref>Если делать сравнение с естественным языком, то терм является тем же самым, что и ''слово'' в предложении.</ref>, а потом соединить их знаками операций, причем он выстраивает операции в некотором порядке, учитывая приоритет.
В Perl термами являются литералы, переменные, выражения в скобках, закавыченная строка символов, а также функции с параметрами в скобках. Только терм может быть операндом. Отметим, что в Perl отсутствуют истинные функции: по существу они являются списковыми, унарными именованными операциями, ведущими себя как функции. Вызывая функцию, мы выполняем списковую или унарную операцию, которая возвращает некоторый результат.
<source lang="perl">
# Следующее выражение демонстрирует списковую операцию. В ней сначала
# вычисляется как терм каждый элемент списка, а затем каждый элемент выводится
# на печать списковой операции.
print 2 + 2 * 25 / 4, ' ', 16; # Напечатает '14.5 16'
# Следующее выражение дает аналогичный результат. Избыточные скобки
# порождают массив, передаваемый операции.
print (2 + 2 * 25 / 4, ' ', 16); # Напечатает '14.5 16'
# Теперь попробуем добавить конкатенацию:
print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 16'
# Обратите внимание, что результат конкатенации потерялся (нет символа '$'
# в выводе). Это произошло потому, что левый операнд конкатенации, как терм
# по приоритету вычисляется первым, т.е. print выводит результат своего терма
# в аргументе, а не результат конкатенации.
# Допустимо
print print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 161$'
# Появление единицы объясняется тем, что внутренний print вернул код 1, который
# был преобразован в строку и был объединен конкатенацией
# c '$'. Так как между внутренним и внешним print нет переноса
# строки, получилась такая склеенная строка.
</source>
При синтаксическом разборе выражений и операторов, как термы рассматриваются конструкции <code>do{}</code> и <code>eval{}</code>, вызовы подпрограмм и методов объектов, анонимные конструкторы массивов скаляров и хешей, а также все операции ввода/вывода.
Еще один немаловажный термин это ''контекст операции''. Операции и термы вычисляются в некотором контексте, от которого зависит поведение операции и возвращаемое ей значение. Существует два основных контекста: ''скалярный'' и ''списковый''. В скалярном контексте интерпретатор пытается вывести из терма скаляр, а в списковом контексте — список литералов <code>qw{}</code>. Контекст определяет обычно тип данных левого операнда. Так, если он является скаляром, то операция выполняется в скалярном контексте; если массив скаляров — списковом контексте. Некоторые операции способны распознавать в каком контексте они исполняются и возвращать свой результат в зависимости от него (универсальные операции).
<source lang="perl">
# В списковой операции print перед списком можно указать выходной дескриптор:
print STDOUT (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '16$'
# Потеря 14.5 Объясняется тем, что левый операнд конкатенации
# находится в скалярном контексте, а в нем операция запятая
# возвращает последний операнд как результат.
# Уберем скобки:
print STDOUT 2 + 2 * 25 / 4, ' ', 16 . '$'; # Напечатает '14.5 16$'
# Теперь каждый элемент списка после вычисления
# передается списковой операции, каждый в скалярном контексте.
</source>
Так как в Perl типизация динамическая<ref>Другими словами, все операции в Perl изначально перегружены.</ref>, понятие контекста операции является аналогом операции ''приведения типов данных'', которая есть в типизированных языках программирования. В большинстве ситуаций Perl знает лучше, в каком контексте вызывать операцию, но иногда приходится ему помогать, используя синтаксические конструкции и специальные функции. Умение предсказывать, в каком контексте выполнится операция в выражении, позволяет программисту Perl придумывать подчас очень запутанные (но с другой стороны короткие) конструкции, которые не могут себе позволить другие языки программирования из-за типизации. Мы рассмотрим некоторые случаи ниже.
<source lang="perl">
# В простом случае левый операнд задает контекст операции.
@arr = ("apple", "pear", "grapes", "banana");
# Левый операнд скаляр, а значит присваивание работает в скалярном контексте.
# В скалярном контексте массивы в правом операнде приводятся к их размеру.
$scalar = @arr;
print "$scalar\n";
# Левый операнд список скаляров, а значит присваивание работает в списковом контексте.
# В списковом контексте присваивание будет копировать элементы из массива в правом операнде
# поэлементно в список в левом операнде. При этом список слева не должен превышать размера массива.
( $element1, $element2, $element3 ) = @arr;
print $element1
. " $element2"
. " $element3\n";
# Если слева будет переменная на массив, операция также будет в списковом контексте,
# и правый массив переопределит значение переменной.
@arr1 = (1..4);
@arr1 = @arr;
print "@arr1\n";
#-------------------------------------------------------------------------------------------------------
# Операция print всегда работает в списковом контексте и все элементы списка пытается интерпретировать
# как строки.
# В стандартной библиотеке Perl есть функция localtime, которая в списковом контексте возвращает
# список из 9 элементов, кодирующих текущую дату.
print localtime; print "\n";
# Выведет примерно такое: 3219121301251120
# Однако, localtime умеет работать в скалярном контексте, в котором выводит дату в формате текущей локали,
# но этот контекст не может использоваться по умолчанию, так как компилятор только знает о том, что
# print умеет работать в списковом контексте.
#
# Этот тот случай, когда мы можем подсказать компилятору выполнить localtime в скалярном контексте,
# чтобы напечатать дату в читаемой форме. Для этого нужно воспользоваться функцией scalar, которая
# принуждает интерпретировать выражение в своем аргументе исключительно в скалярном контексте.
print scalar localtime; print "\n";
# В локали LANG_EN будет печататься в формате "Mon Jan 13 12:32:17 2025".
# Можно поступить проще и привести компилятор к скалярному контексту через другую операцию, которая
# этот контекст использует. Например, операция конкатенации строк всегда работает в скалярном контексте.
# Если компилятор увидит выражение, использующее скалярный контекст, он будет использоваться для всех
# операндов операции. Таким образом, такого же результата мы добиваемся, если напишем так:
print localtime . "\n";
# "Mon Jan 13 12:32:17 2025"
#-------------------------------------------------------------------------------------------------------
# Рассмотрим более сложный пример.
# Пусть дана такая строка:
$word = "111example222";
# Вопрос: как посчитать в ней количество символов цифр и букв и совместить это с печатью в одном компактном выражении?
# Для ответа на вопрос "сколько символов данного класса?" здесь следует использовать регулярные выражения.
# Так как о регулярных выражениях мы будем говорить много позже, возьмите на веру, что регулярное выражение для
# поиска цифр выглядит так '/\d/g', а букв так '/[^\d\s]/g'.
#
# Примечание: '/[^\d\s]/g' не учитывает Unicode символы.
#
# Проблема кроется в том, что регулярные выражения умеют работать и в скалярном, и в списковом контекстах.
# Конкретно нам нужен списковый контекст, в котором операция возвращает массив совпадений по шаблону, который
# мы могли бы преобразовать в размер, тем самым ответив на вопрос "сколько?". Но как нам сказать, чтобы операция
# выполнялась в списковом контексте, а не скалярном, ведь специальной функции типа scalar для этого нет?
#
# Единственный способ сделать это, привести компилятор к этому контексту через другую операцию.
# Существует два распространенных приёма провоцировать списковый контекст:
# * Можно использовать операцию присваивания, в которой левый операнд это пустой массив.
# * Можно использовать операцию разыменовывания ссылки, которая будет работать в контексте типа
# разыменовывания.
# Ниже мы в одном выражении используем оба приема для демонстрационных целей
print "Number of letters: "
. ( () = $word =~ /[a-zA-Z]/g ) . "\n"
. "Number of digits: "
. ( @{ [ $word =~ /\d/g ] } ) . "\n"
;
# Number of letters: 7
# Number of digits: 6
</source>
Не расстраивайтесь, если вы ничего из предыдущих примеров не поняли, так как мы сильно забегаем в них вперед. Эти примеры призваны показать, как сильно выражения завязаны на контекст операции.
Скалярный контекст также можно разделить по типу хранимых данных на ''числовой'', ''строковый'' и ''безразличный''. Perl неспособен заранее распознать этот контекст, поэтому он просто подключает механизм неявного приведения данных, когда скаляры в операндах хранят разные типы.
Еще один специальный скалярный контекст — ''булевый''. Так как в Perl нет булева <code>true</code> или <code>false</code>, Perl анализирует строки и числа в булевом контексте, как это было описано выше: <code>0</code>, <code>"0"</code>, <code>"00"</code>, <code>"0.0"</code> и <code>""</code> это условно ЛОЖЬ, а все остальное ИСТИНА.
Еще одним контекстом является void-контекст, который используется, когда в выражении нет побочных эффектов. Так, вы можете спокойно писать в Perl такие бессмысленные конструкции (они выполняются в void-контексте):
<source lang="perl">
(1,2,3);
$n;
"Hello";
</source>
Этот контекст можно обнаружить, если интерпретатор Perl запущен в режиме вывода предупреждающих сообщений.
Еще один контекст это ''подстановочный контекст'', в котором вычисляются операции заключения в кавычки (кроме одинарных кавычек). В этом контексте подставляются переменные и интерпретируются управляющие последовательности.
Ниже для справки представлены приоритеты операций.
{|class="wikitable" style="width:100%;"
|-
! Приоритет
! Операция
! Сочетаемость
|-
|style="width:10%;"|1
|Вычисление термов и левосторонних списковых операций
|style="width:20%;"|Слева направо
|-
|2
|<code><nowiki>-></nowiki></code>
|Слева направо
|-
|3
|<code><nowiki>++</nowiki></code> <code><nowiki>--</nowiki></code>
|Не сочетаются
|-
|4
|<code><nowiki>**</nowiki></code>
|Справа налево
|-
|5
|<code><nowiki>!</nowiki></code> <code><nowiki>~</nowiki></code> <code><nowiki>\</nowiki></code> унарный <code><nowiki>+</nowiki></code> унарный <code><nowiki>-</nowiki></code>
|Справа налево
|-
|6
|<code><nowiki>=~</nowiki></code> <code><nowiki>!=</nowiki></code>
|Слева направо
|-
|7
|<code><nowiki>*</nowiki></code> <code><nowiki>/</nowiki></code> <code><nowiki>%</nowiki></code> <code><nowiki>x</nowiki></code>
|Слева направо
|-
|8
|<code><nowiki>+</nowiki></code> <code><nowiki>-</nowiki></code> <code><nowiki>.</nowiki></code>
|Слева направо
|-
|9
|<code><nowiki><<</nowiki></code> <code><nowiki>>></nowiki></code>
|Слева направо
|-
|10
|Именованные унарные операции
|Не сочетаются
|-
|11
|<code><nowiki><</nowiki></code> <code><nowiki>></nowiki></code> <code><nowiki><=</nowiki></code> <code><nowiki>>=</nowiki></code> <code><nowiki>lt</nowiki></code> <code><nowiki>gt</nowiki></code> <code><nowiki>le</nowiki></code> <code><nowiki>ge</nowiki></code>
|Не сочетаются
|-
|12
|<code><nowiki>==</nowiki></code> <code><nowiki>!=</nowiki></code> <code><nowiki><=></nowiki></code> <code><nowiki>eq</nowiki></code> <code><nowiki>ne</nowiki></code> <code><nowiki>cmp</nowiki></code>
|Не сочетаются
|-
|13
|<code><nowiki>&</nowiki></code>
|Слева направо
|-
|14
|<code><nowiki>|</nowiki></code> <code><nowiki>^</nowiki></code>
|Слева направо
|-
|15
|<code><nowiki>&&</nowiki></code>
|Слева направо
|-
|16
|<code><nowiki>||</nowiki></code>
|Слева направо
|-
|17
|<code><nowiki>..</nowiki></code> <code><nowiki>...</nowiki></code>
|Не сочетаются
|-
|18
|<code><nowiki>?:</nowiki></code>
|Справа налево
|-
|19
|<code><nowiki>=</nowiki></code> <code><nowiki>**=</nowiki></code> <code><nowiki>+=</nowiki></code> <code><nowiki>-=</nowiki></code> <code><nowiki>.=</nowiki></code> <code><nowiki>*=</nowiki></code> <code><nowiki>/=</nowiki></code> <code><nowiki>%=</nowiki></code> <code><nowiki>x=</nowiki></code> <code><nowiki>&=</nowiki></code> <code><nowiki>|=</nowiki></code> <code><nowiki>^=</nowiki></code> <code><nowiki><<=</nowiki></code> <code><nowiki>>>=</nowiki></code> <code><nowiki>&&=</nowiki></code> <code><nowiki>||=</nowiki></code>
|Справа налево
|-
|20
|<code><nowiki>,</nowiki></code> <code><nowiki>=></nowiki></code>
|Слева направо
|-
|21
|Правосторонние списковые операции
|Не сочетаются
|-
|22
|<code><nowiki>not</nowiki></code>
|Справа налево
|-
|23
|<code><nowiki>and</nowiki></code>
|Слева направо
|-
|24
|<code><nowiki>or</nowiki></code> <code><nowiki>xor</nowiki></code>
|Слева направо
|}
* В данной таблице чем выше приоритет операции и чем левее она стоит (в большинстве случаев), тем раньше она выполняется.
* Приоритет списковой операции зависит от ее места в выражении и может изменяться от наивысшего к наименьшему. Все операции в выражении, стоящие слева от списковой, имеют наивысший приоритет по отношению к ней (смотрите 21 приоритет).
* Свойство сочетаемости означает, что операции одного приоритета могут быть в одном выражении, причем порядок их разрешения может быть слева направо или справа налево.
* Скобки позволяют изменить порядок вычисления, определяемому в обычных условиях приоритетом и сочетаемостью. Скобки всегда наделяют выражение в них наивысшим приоритетом.
== Примечания ==
{{примечания}}
gyfdbi4jj3mr716dso2ubaynbztdho8
269930
269929
2026-09-02T16:30:46Z
ScriptMaster
15708
/* Операции присваивания */
269930
wikitext
text/x-wiki
{{Навигация учебника}}
''Операция'' — это некоторые действия над операндами, результатом которых является новое значение. В Perl есть десятки операций, большинство из которых мы рассмотрим в этой главе, а другие в последующих. Операции можно делить по типу выполняемых ими действий (например, арифметические, логические и т.д.), по объекту, на который они воздействуют (строковые, списковые, файловые), а иной раз сложно отнести их к какой-то категории.
Большинство операций работает с двумя операндами, которые записываются по обе стороны от знака операции (''бинарная операция''). Например в операции сложения операндами выступают слагаемые. Для других операций может требоваться один операнд (''унарная операция'') или три (''тернарная операция''). В некоторых операциях какие-то операнды можно опускать.
Серия операций, записанная единым куском образует ''выражение''. Любое выражение интерпретируется слева направо, при этом порядок исполнения операций внутри выражения зависит от приоритета операций. Приоритеты операций в выражении можно менять через круглые скобки, которые дают более высокий приоритет.
== Арифметические операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Арифметические операции'''</center>
|-
|<center><code>+</code></center>
|style="width:10%;"|<center><code>6.35+8.31</code></center>
|style="width:30%;"|'''Сложение'''. Выполняет арифметическое сложение
|<center><code>-</code></center>
|style="width:10%;"|<center><code>9.15-5</code></center>
|style="width:30%;"|'''Вычитание'''. Выполняет арифметическое вычитание
|-
|<center><code>*</code></center>
|<center><code>2*2</code></center>
|'''Умножение'''
|<center><code>/</code></center>
|<center><code>15/3</code></center>
|'''Деление'''
|-
|<center><code>**</code></center>
|<center><code>4**2</code></center>
|'''Возведение в степень'''
|<center><code>%</code></center>
|<center><code>10%5</code></center>
|'''Остаток от деления'''
|-
| colspan="6" | <center>'''Унарные арифметические операции'''</center>
|-
|<center><code>+</code></center>
|<center><code>+5</code></center>
|'''Унарный плюс'''
|<center><code>-</code></center>
|<center><code>-6</code></center>
|'''Унарный минус'''. Делает положительное число отрицательным, а отрицательное — положительным
|-
| colspan="6" | <center>'''Инкремент/декремент'''</center>
|-
|<center><code>++</code></center>
|<center><code>$i = 5, $j=$i++;</code></center>
|'''Постфиксный инкремент'''. Увеличивает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 6, потому что инкремент выполняется после присваивания
|<center><code>++</code></center>
|<center><code>$i = 5, $j=++$i;</code></center>
|'''Префиксный инкремент'''. В отличие от постфиксного инкремента, значение в операнде будет увеличено до всех прочих операций над ним. В данном примере в обеих переменных будет 6
|-
|<center><code>--</code></center>
|<center><code>$i = 5, $j=$i--;</code></center>
|'''Постфиксный декремент'''. Уменьшает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 4, потому что декремент выполняется после присваивания
|<center><code>--</code></center>
|<center><code>$i = 5, $j=--$i;</code></center>
|'''Префиксный декремент'''. В отличие от постфиксного декремента, значение в операнде будет уменьшено до всех прочих операций над ним. В данном примере в обеих переменных будет 4
|}
* В основном арифметические операции направлены на работу с числами, однако операндами могут выступать и строки, причем не обязательно, чтобы они конвертировались в числа. В таких случаях интерпретатор сначала попытается выделить число из символов, начиная с первого. Если выделить число не получается, то результатом конвертации будет 0. Так, строка <code>5gh4</code> будет конвертирована в 5, а <code>g130</code> — в 0.
* Если в операции остаток от деления (<code>%</code>) второй операнд является вещественным числом и имеет дробную часть, то он предварительно преобразуется в целое число путем простого отброса дробной части.
* Отрицательное число <u>нельзя</u> возводить не в целую степень. Интерпретатор не будет выдавать никакой ошибки, но при этом результат всегда будет 0.
* Бинарные арифметические операции выполняются в ''скалярном контексте''. Это означает, что интерпретатор будет ожидать в операндах скалярные переменные. Если в операндах будут массивы, то они будут преобразованы к числу элементов в массиве, а в случае хеш-массива — к числу использованных хеш-записей в нем.
* Инкремент и декремент можно применять к строковым данным. В случае инкремента, если строка состоит только из латинских букв, то результатом будет новая строка, в которой последняя буква будет заменена на следующую по порядку в алфавите, причем строчная буква заменяется строчной, а прописная — прописной. Аналогично происходит, если строка завершается цифрой. Для декремента Perl будет пытаться конвертировать строку в число по обычным правилам, описанным выше.
== Строковые операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Строковые операции'''</center>
|-
|<center><code>.</code></center>
|style="width:10%;"|<center><code>"a"."b"</code></center>
|style="width:30%;"|'''Конкатенация строк'''. Объединяет две строки в одну
|<center><code>x</code></center>
|style="width:10%;"|<center><code>"a" x 3</code></center>
|style="width:30%;"|'''Повторитель'''. Повторяет строку в левом операнде N число раз и возвращает из этого строку
|}
* Строковые операции обычно направлены на строковый тип данных, однако можно использовать и числа. В этом случае числа будут преобразованы в строки.
* Оператор конкатенации не вставляет пробелы между строками. Обычно для объединения с пробелами прибегают к функции <code>join</code>.
* Для операции ''повторения'' левый операнд всегда приводится к строковому значению. Правым операндом должно быть целое число, либо строка, которая может быть приведена к целому числу. В случае вещественного числа, дробная часть будет просто отбрасываться.
* Для операции ''повторения'' левым операндом может быть список слов, заключенный в круглые скобки, т.е. эта операция может работать в ''списковом контексте''. В таком случае список будет повторен указанное число раз. Такой прием используется для порождения списка, передаваемого конструктору массива.
* Знак операции повторения следует всегда отделять хотя бы одним пробелом от его операндов, так как в некоторых ситуациях интерпретатор может принять его за часть имени переменной.
* С операцией повторения связана устойчивая идиома, используемая для быстрого заполнения одинаковыми значениями:<source lang="perl">
@arr[1..5] = (1) x 5;
print "@arr\n";
# 1 1 1 1 1
@hash{qw{ apples bananas pears }} = (0) x 3;
print "@{[%hash]}\n";
# apples 0 bananas 0 pears 0
</source>Если в левом операнде операции повторения используется массив, то операция будет выполняться в списковом контексте, в котором она повторит элементы указанное число раз в новом массиве. Если этот массив присвоить срезу массива или хэша (слева от присваивания), то сгенерированные повторением элементы будут присвоены элементам среза. На практике часто используется прием получения хеша из параметров командной строки, переданных сценарию:<source lang="perl">
my %exists;
@exists{@ARGV} = (1) x @ARGV;
# \ \____ В скалярном контексте развернется в число элементов массива @ARGV.
# \___________________ Получается хеш, в котором ключами будут элементы командной строки.
</source>
== Логические операции ==
=== Операции отношения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Числовые отношения'''</center>
|-
|<center><code>></code></center>
|style="width:10%;"|<center><code>2>1</code></center>
|style="width:30%;"|'''Больше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>>=</code></center>
|style="width:10%;"|<center><code>3>=1</code></center>
|style="width:30%;"|'''Больше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><</code></center>
|<center><code>-3<0</code></center>
|'''Меньше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code><=</code></center>
|<center><code>0<=3</code></center>
|'''Меньше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>==</code></center>
|<center><code>3==3</code></center>
|'''Равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>!=</code></center>
|<center><code>1!=2</code></center>
|'''Не равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><=></code></center>
|<center><code>15<=>63</code></center>
|'''Один из операндов больше другого'''. Возвращает -1, если правый операнд больше левого; 1 — правый операнд меньше левого; 0 — операнды равны
|
|
|
|-
| colspan="6" | <center>'''Строковые отношения'''</center>
|-
|<center><code>eq</code></center>
|<center><code>"a" eq "a"</code></center>
|'''Строки лексикографически равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ne</code></center>
|<center><code>"a" ne "b"</code></center>
|'''Строки лексикографически не равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>lt</code></center>
|<center><code>"a" lt "c"</code></center>
|'''Левая строка лексикографически меньше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>le</code></center>
|<center><code>"a" le "c"</code></center>
|'''Левая строка лексикографически меньше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>gt</code></center>
|<center><code>"d" gt "e"</code></center>
|'''Левая строка лексикографически больше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ge</code></center>
|<center><code>"d" ge "e"</code></center>
|'''Левая строка лексикографически больше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>cmp</code></center>
|<center><code>"a" cmp "b"</code></center>
|'''Лексикографически один из операндов не равен другому'''. Возвращает -1, если правый операнд лексикографически больше левого; 1 — правый операнд лексикографически меньше левого; 0 — операнды лексикографически равны
|
|
|
|}
* Обратите внимание, что операции отношения скопированы с командной оболочки Bourne Shell, но там все было наоборот: <code>eq</code>, <code>ne</code>, <code>lt</code>, <code>le</code>, <code>gt</code> и <code>ge</code> применялись к числам, а привычные нам знаки использовались для лексикографического сравнивания. Об этом нужно помнить, когда вы переключаетесь с программирования для командной оболочки на программирование для Perl.
* Лексикографическое сравнивание означает, что сравниваются ASCII кодировки символов. Так символ <code>a</code> лексикографически будет меньше символа <code>b</code>, потому что код последнего (98) больше первого (97). При лексикографическом сравнивании строк учитывается код каждого символа в строке, а также длина строк.
* ИСТИНА для числовых отношений это число 1, а для строковых — строка <code>"1"</code>.
* В общем случае следует быть осторожным при сравнивании вещественных чисел с целыми, так как тут вступает фактор округления чисел. Следует отметить, что вещественные числа вообще нельзя сравнивать на равенство, так как результат практически всегда будет неправильным. В этом случае следует сравнивать разницу двух вещественных чисел с очень малым числом (погрешностью). Можно использовать и другой приём, когда числа преобразуются в строки и сравниваются как строки.
=== Булевы выражения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Булевы операции'''</center>
|-
|<center><code>&&</code></center>
|style="width:10%;"|<center><code>1 && 1</code></center>
|style="width:30%;"|'''Конъюнкция'''
|<center><code><nowiki>||</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>0 || 1</nowiki></code></center>
|style="width:30%;"|'''Дизъюнкция'''
|-
|<center><code>!</code></center>
|<center><code>! 0</code></center>
|'''Логическое отрицание'''
|<center><code>xor</code></center>
|<center><code>1 xor 1</code></center>
|'''Исключающее ИЛИ'''
|-
|<center><code>and</code></center>
|<center><code>1 and 1</code></center>
|'''Конъюнкция'''. ''Синоним''
|<center><code>or</code></center>
|<center><code>0 or 1</code></center>
|'''Дизъюнкция'''. ''Синоним''
|-
|<center><code>not</code></center>
|<center><code>not 1</code></center>
|'''Логическое отрицание'''. ''Синоним''
|<center><code>^^</code></center>
|<center><code>1 ^^ 1</code></center>
|'''Исключающее ИЛИ'''<ref>Знак операции появился с версии интерпретатора 5.40.</ref>
|-
|<center><code>//</code></center>
|<center><code>$a // $b</code></center>
|'''Слияние'''
|
|
|
|}
* Операции <code>or</code>, <code>and</code>, <code>not</code> и <code>xor</code> появились в Perl, начиная с версии 5.001. Отличие их от аналогичных операций в виде символов в том, что они имеют меньший приоритет при вычислении в сложных выражениях. Такой пониженный приоритет иногда требуется в выражениях по типу <code>$num != 0 or print "\$num == 0";</code>
* Для булевых операций ИСТИНОЙ признается любое скалярное значение не равное <code>0</code>, <code>"0"</code> и <code>""</code>, в противном случае это ЛОЖЬ. Значение <code>undef</code> также приравнивается ко ЛЖИ.
* Булевы операции конъюнкции и дизъюнкции вычисляются по укороченной схеме. Это означает, например, что если при конъюнкции ЛОЖЬ в некотором операнде встречается раньше, чем закончилось выражение, вычисление прекращается на этом операнде с общим результатом ЛОЖЬ.
* Хотя булевы операции рассчитаны в основном на то, чтобы использовать их с числами, некоторые из них можно использовать для работы со строками. Полезной здесь оказывается дизъюнкция, которая может быть использована для записи укороченного присваивания значений по умолчанию, что показано в следующем примере: <source lang="perl">
# Эта процедура просто возвращает строку, переданную в аргументе, но если он не передается,
# то она назначит ему значение по умолчанию.
sub procedure {
my ($arg) = @_;
$arg ||= "default_value";
return $arg;
}
# Вызов без аргумента
print "Result: " . procedure . "\n";
# Result: default_value
# Вызов с аргументом
print "Result: " . procedure "some_value" . "\n";
# Result: some_value
</source> Также, в случаях, когда неопределенные значения недопустимы, она может использоваться для выбора из альтернативы: <source lang="perl">
$var = undef;
$var = $var || "default_value";
print "$var\n";
# default_value
# Размер цепочки может быть сколько угодно большим. В данном примере мы использовали
# undef для демонстрации, но представьте на их местах переменные. Значение по умолчанию
# выбирается с разным приоритетом (первое не пустое с левого края).
$value = undef || undef || undef || "default_value";
print "$value\n";
</source>
* Операция слияния (<code>//</code>) работает так: если левый операнд определен (''defined''), то вычисляется выражение, записанное в первом операнде и его результат возвращается как результат всей операции, иначе, если левый операнд не определен, но определен второй операнд, то его результат возвращается как результат всей операции. Если не определены оба операнда, то возвращается <code>undef</code> как результат всей операции. Технически операция похожа на тернарную операцию (см. ниже), но нацелена она на проверку условия ''определен'' / ''не определен''. Операция имеет пониженный приоритет, поэтому в сложных выражениях её обычно следует оставлять в скобках. Вот примеры, когда она может использоваться:<source lang="perl">
# Взять значение в кеше или вызвать процедуру по извлечению значения
# из хранилища, если значение в нем пусто.
$data = $cache{ $key } // get_from_storage($key);
# ----------------------------------------------------------
$x = 10;
$y = undef;
$z = 5;
# Это выражение ...
$result = $x + ($y // $z);
# ... является коротким способом записать такое
unless (defined $y) {
$result = $x + $z;
} else {
$result = $x + $y;
}
# ----------------------------------------------------------
$discounted_price = 60;
$base_price = 100;
$tax_rate = undef;
$price = $discounted_price // $base_price;
# Если налоговая ставка не определена, то она равняется нулю.
$final = $price * (1 - ($tax_rate // 0));
# ----------------------------------------------------------
$default_config = 'default';
# Будет присвоено первое определенное значение.
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# ----------------------------------------------------------
$default_config = undef;
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# Процедура compute_expensive_value будет вызвана только если config_value
# не определена.
$config_value //= compute_expensive_value();
sub compute_expensive_value {
'computed'
}
print "$config_value\n";
</source>
=== Побитовые операции ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Побитовые операции'''</center>
|-
|<center><code><nowiki>&</nowiki></code></center>
|style="width:10%;"|<center><code>3 & 2</code></center>
|style="width:30%;"|'''Побитовое И'''. Выполняет конъюнкцию двух чисел побитово
|<center><code><nowiki>|</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>3 | 2</nowiki></code></center>
|style="width:30%;"|'''Побитовое ИЛИ'''. Выполняет дизъюнкцию двух чисел побитово
|-
|<center><code>^</code></center>
|<center><code>3 ^ 3</code></center>
|'''Побитовое исключающее ИЛИ'''
|<center><code>~</code></center>
|<center><code>~0</code></center>
|'''Побитовое отрицание'''
|-
|<center><code><<</code></center>
|<center><code>1 << 1</code></center>
|'''Побитовый сдвиг влево'''
|<center><code>>></code></center>
|<center><code>2 >> 1</code></center>
|'''Побитовый сдвиг вправо'''
|}
* Побитовые операции работают только с числами, если хотя бы один операнд является числом (соответственно другой будет сконвертирован в число). Если '''оба''' операнда являются строками, то побитово выполняются операции с кодом каждого символа, даже если строки записаны только цифрами. Если при этом строки разной длины, то меньшая из них дополняется символами <code>\000</code> до равенства.
* Все вещественные числа преобразуются к целым.
* Допустимо использовать любой формат представления чисел: десятичный, шестнадцатеричный и восьмеричный. Язык Perl представляет числа с длиной 32 бит для поразрядных операций, независимо от архитектуры компьютера.
== Операции присваивания ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Операции присваивания'''</center>
|-
|<center><code>=</code></center>
|style="width:10%;"|<center><code>$i = 2</code></center>
|style="width:30%;"|'''Простое присваивание'''
|<center><code>**=</code></center>
|style="width:10%;"|<center><code>$i **= 2</code></center>
|style="width:30%;"|'''Возведение в степень с присваиванием'''
|-
|<center><code>+=</code></center>
|<center><code>$i += 2</code></center>
|'''Сложение с присваиванием'''
|<center><code>-=</code></center>
|<center><code>$i -= 2</code></center>
|'''Вычитание с присваиванием'''
|-
|<center><code>.=</code></center>
|<center><code>$i .= 2</code></center>
|'''Конкатенация с присваиванием'''
|<center><code>*=</code></center>
|<center><code>$i *= 2</code></center>
|'''Умножение с присваиванием'''
|-
|<center><code>/=</code></center>
|<center><code>$i /= 2</code></center>
|'''Деление с присваиванием'''
|<center><code>%=</code></center>
|<center><code>$i %= 2</code></center>
|'''Остаток от деления с присваиванием'''
|-
|<center><code>x=</code></center>
|<center><code>$i x= 2</code></center>
|'''Повторение с присваиванием'''
|<center><code>&=</code></center>
|<center><code>$i &= 2</code></center>
|'''Поразрядное И с присваиванием'''
|-
|<center><code><nowiki>|=</nowiki></code></center>
|<center><code><nowiki>$i |= 2</nowiki></code></center>
|'''Поразрядное ИЛИ с присваиванием'''
|<center><code>^=</code></center>
|<center><code>$i ^= 2</code></center>
|'''Поразрядное исключающее ИЛИ с присваиванием'''
|-
|<center><code><<=</code></center>
|<center><code>$i <<= 2</code></center>
|'''Поразрядный сдвиг влево с присваиванием'''
|<center><code>>>=</code></center>
|<center><code>$i >>= 2</code></center>
|'''Поразрядный сдвиг вправо с присваиванием'''
|-
|<center><code>&&=</code></center>
|<center><code>$i &&= 2</code></center>
|'''Конъюнкция с присваиванием'''
|<center><code><nowiki>||=</nowiki></code></center>
|<center><code><nowiki>$i ||= 2</nowiki></code></center>
|'''Дизъюнкция с присваиванием'''
|-
|<center><code>^^=</code></center>
|<center><code>$i ^^= 1</code></center>
|'''Исключающее ИЛИ с присваиванием'''<ref>Поддерживается с версии 5.42.</ref>
|<center><code>//=</code></center>
|<center><code>$value //= $default</code></center>
|'''Слияние с присваиванием'''
|}
* Простое присваивание является бинарной операцией, правый операнд которой является выражением, а левый — определяет участок памяти (в форме переменной), в который запишется результат выражения. Операция присваивания возвращает левый операнд как результат выражения. По этой причине следующая запись будет синтаксически правильной<source lang="perl">
($i = $j) = 5;
# Если развернуть эту конструкцию, то получается
$i = $j;
$i = 5; # Так как выражение в скобках вернет $i
</source>
* Как в языке Си, в Perl есть составное присваивание, совмещающее в себе операцию и присваивание. Операции с присваиванием также возвращают левый операнд, как результат выражения<source lang="perl">
$b = 1;
$a = ($b += 3); # Аналогично $a = $b = 4
$a += ($b += 3); # Аналогично $a = $a + $b + 3
(($a += 2) **= 2 ) -= 1; # Аналогично $a = ($a + 2) ** 2 - 1
</source>Обратим внимание, что в большинстве ситуаций в сложных выражениях ставить скобки для операций присваивания обязательно, потому что, как вы узнаете ниже, они парсятся интерпретатором справа налево.
== Списковые операции ==
''Списковая операция'' — это операция над списком значений. Операции, которым передаются списки и кажущиеся функциями, на самом деле являются списковыми операциями, например <code>print</code>. Это связано с тем, что списковые операции разрешают помещать свои аргументы в скобки. Так, следующие вызовы равнозначны
<source lang="perl">
print $a, "hello", $b;
print ($a, "hello", $b);
</source>
Списковые операции ожидают на входе список скаляров в некоторой форме, а на выходе отдают тот же список, но модифицированный некоторым образом, либо новый список. Так, следующие операции являются списковыми:
* Создание и конкатенация массивов<source lang="perl">
@array1 = (1,2,3);
@array2 = (4,5,6);
@array3 = (@array1, @array2);
</source>
* Встроенные операции, связанные с манипуляцией значением(ми) в массиве<source lang="perl">
@array = (1,2,3);
push @array, 4; # (1, 2, 3, 4)
pop @array; # (1, 2, 3); 4 возвращается как результат
shift @array; # (2, 3); 1 возвращается как результат
unshift @array, 5; # (5, 2, 3)
splice @array, 0, 2; # (3); (5, 2) возвращается как результат
</source>
* Трансформация (новый массив возвращается как результат)<source lang="perl">
@array = qw { a b c d e f };
# Применение предиката к каждому элементу в списке
print "$_\n" foreach map { $_ . $counter++ } @array;
#
# Вызов map в модификаторе foreach возвращает новый массив, каждый элемент которого
# печатается print слева от модификатора. В этом примере процедура предиката к каждому
# элементу исходного массива добавляет порядковый номер, в результате чего получается
#
# a0
# b1
# c2
# d3
# e4
# f5
#
# Сформировать новый массив, элементы которого хранятся в обратном порядке
print "$_\n" foreach reverse @array;
# f
# e
# d
# c
# b
# a
# Сортировка
use List::Util; # Подключаем модуль для перемешивания элементов для дальнейшего примера.
@array = List::Util::shuffle(@array); # Перемешиваем элементы в случайном порядке.
print "@{array}\n"; # Вывод будет отличаться при каждом вызове: "b f d a e c"
@array = sort @array; # Снова в алфавитном порядке.
print "@{array}\n"; # "a b c d e f"
</source>
* Фильтрующие операции (возвращают подмножество исходного массива как результат)<source lang="perl">
@array = qw { a 1 5 d 6 f 8 9 h j k l 25 };
@numbers = grep /\d+/, @array; # Достаем из исходного массива только цифры
print "@{numbers}\n"; # "1 5 6 8 9 25"
@letters = grep /[a-z]+/, @array; # Достаем из исходного массива только буквы
print "@{letters}\n"; # "a d f h j k l"
</source>
* Агрегирующие операции (как правило, возвращают все элементы массивов одним значением)<source lang="perl">
@words = qw { apple grapes banana };
$value = join ", ", @words; # объединяет все элементы и вставляет между ними разделитель.
print "${value}\n"; # "apple, grapes, banana"
</source>
* Разворачивающие операции (наоборот, из одного значения получают список)<source lang="perl">
@numbers = 1..5;
print "@{numbers}\n"; # "1 2 3 4 5"
$fruits = "apple, banana, grapes";
@fruits = split ", ", $fruits;
print "@{fruits}\n"; # "apple banana grapes"
</source>
Подробнее о пользовательских списковых операциях будет в главе, посвященной функциям.
=== Списковые операции all и any ===
Начиная с версии 5.42, в ядро интерпретатора были интегрированы две новые операции, помогающие работать с множествами без использования модулей. До этого эти операции приходилось подключать в виде функций из модуля <code>List::Util</code>. Вероятно, добавление этих операций в ядро было вдохновлено языком Python, в котором они существуют в виде операторов.
<source lang="perl">
any { <блок-предиката> } @list;
all { <блок-предиката> } @list;
</source>
Операции <code>all</code> и <code>any</code> мимикрируют под операцию <code>grep</code> и используются, чтобы вернуть логический ответ по предикату. Чтобы операция <code>all</code> вернула ИСТИНУ, необходимо чтобы её предикат вернул ИСТИНУ для каждого элемента списка. Соответственно, для операции <code>any</code> ИСТИНОЙ считается ситуация, когда предикат возвращает ИСТИНУ по меньшей мере для одного элемента списка.
Появление этих операций, хоть и с опозданием, очень кстати, так как различного рода проверки множеств — частая задача на практике. Вот лишь несколько примеров.
<source lang="perl">
use v5.42; # Директива требует интерпретатор не ниже указанной версии.
# Следующие директивы нужны по крайней мере для версии 5.42, так как на тот момент
# эти возможности были еще экспериментальными. В будущих версиях эти
# директивы не понадобятся.
use feature 'keyword_any', 'keyword_all'; # Включает операции
no warnings 'experimental::keyword_any', 'experimental::keyword_all'; # Отключает предупреждения
# Использование my в этом месте обусловлено тем, что директива use v5.42
# автоматически включает директиву strict (будет рассмотрено позже).
my @numbers = (2, 4, 6, 8, 10);
# Через специальную переменную $_ предикат получает очередной элемент списка.
if (all { $_ % 2 == 0 } @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Сравните этот код с программой, которую мы бы написали для версии интерпретатора меньше 5.42:
<source lang="perl">
use List::Util qw{ all any }; # Импортируем функции из модуля.
@numbers = (2, 4, 6, 8, 10);
# ____ Запятую можно опустить, так как у функции есть прототип. Для any она опущена.
# V
if (all { $_ % 2 == 0 }, @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Обратите внимание на то, что в старых версиях интерпретатора функции <code>all</code> и <code>any</code> всегда нужно импортировать из модуля <code>List::Util</code>. Однако не спешите полностью списывать этот модуль, так как в нём есть множество других полезных функций.
== Операции заключения в кавычки ==
Кавычки (одинарные, двойные и обратные) используются в Perl, чтобы задавать строковые литералы. Кроме этого есть еще операции, которые внешне похожи на операции заключения в кавычки, и перечислены в таблице ниже.
{|class="wikitable" style="width:100%;"
|-
! Операция
! Эквивалент
! Описание
! Операция
! Эквивалент
! Описание
|-
| colspan="6" | <center>'''Операции заключения в кавычки'''</center>
|-
|<center><code>q{}</code></center>
|style="width:10%;"|<center><code><nowiki>''</nowiki></code></center>
|style="width:30%;"|'''Строковый литерал'''. Подстановки невозможны
|<center><code>qq{}</code></center>
|style="width:10%;"|<center><code>""</code></center>
|style="width:30%;"|'''Строковый литерал'''
|-
|<center><code>qx{}</code></center>
|<center><code><nowiki>``</nowiki></code></center>
|'''Команда системы'''
|<center><code>qw{}</code></center>
|<center><code><nowiki>()</nowiki></code></center>
|'''Список слов'''. Подстановки невозможны
|-
|<center><code>m{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Поиск по регулярному выражению'''
|<center><code>qr{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Шаблон регулярного выражения'''
|-
|<center><code>s{}{}</code></center>
|<center><code>s///</code></center>
|'''Подстановка'''
|<center><code>tr{}{}</code></center>
|<center><code><nowiki>y///</nowiki></code></center>
|'''Транслитерация'''. Подстановки невозможны
|}
* Фигурные скобки в данных операциях представляют собой разделитель. Они могут быть в любой момент заменены на другой символ или любую другую пару скобок, например<source lang="perl">
q{hello}
q/hello/
q!hello!
q<\<hello\>> # Если символы разделителя есть в значении, то они должны экранироваться.
</source>Внутри разделителей разрешается любое число пробелов, а также внутри большинства операций разрешены подстановки, кроме некоторых (указанных в таблице отдельно).
* Операция <code>qw{}</code> позволяет вернуть список слов. Чаще всего она используется, чтобы инициализировать массив.<source lang="perl">
@arr = qw{ one two three };
# Это аналогично
@arr = ("one", "two", "three");
# и
$arr[0] = "one";
$arr[1] = "two";
$arr[2] = "three";
</source>Обратите внимание, что внутри <code>qw{}</code> запятые ставить не нужно: разделителями являются пробелы.
* Об остальных операциях будет рассказано далее по ходу изложения.
=== Операция Here-docs ===
В Perl перекочевала Here-docs операция, придуманная для командной оболочки Bourne Shell, которая позволяет инициализировать переменные многострочными строками, либо передавать их на вход некоторой операции, записывая строки прямо в исходном файле программы. Как и в Bourne Shell она записывается так
<source lang="perl">
$str = <<EOF;
строка 1
строка 2
строка 3
EOF
print <<END;
Hello, World!
END
</source>
Здесь после знака <code><<</code> вводится так называемое стоп-слово. Далее записывается многострочная строка. Интерпретатор будет искать стоп-слово, для того чтобы понять где заканчивается эта строка. Стоп-слово может быть любым строковым литералом. В наших примерах стоп-словами были <code>EOF</code> и <code>END</code>.
За стоп-словом обычно следует ставить символ точки с запятой, так как Here-docs является простым оператором, конец которого находится после ввода стоп-слова. Пропуск точки с запятой является очень частой ошибкой. Тем не менее, если оператор Here-docs является единственным или последним в текущем блоке, точку с запятой можно опускать.
По умолчанию многострочная строка записывается так, как будто она находится в двойных кавычках. Таким образом, в ней будут разрешаться все подстановки и управляющие последовательности. Вы можете явно обозначить, как должен поступать интерпретатор, если будете использовать соответствующие кавычки вокруг стоп-слова.
<source lang="perl">
$var = "Hello";
print <<END;
\t$var, World!
END
# Аналогично предыдущему вызову
print <<"END";
\t$var, World!
END
# С одинарными кавычками переменные перестанут подставляться и
# не будут работать управляющие последовательности
print <<'END';
\t$var, World!
END
</source>
Можно также использовать наклонные кавычки, тогда в Here-docs можно писать процедуры командной оболочки и инициализировать переменные результатом работы этих процедур. Каждая строка в таких процедурах это отдельный ввод команды в оболочку. Следует помнить, что результаты могут зависеть от операционной системы, в которой исполняется Perl.
<source lang="perl">
# Выведет каталоги корневой точки монтирования в *nix системе.
print <<`END`;
ls -l /
END
</source>
Если стоп-слово вводится без кавычек, то нельзя ставить пробелы между <code><<</code> и стоп-словом. Если такое случится, то Perl не распознает такого слова и будет захватывать строки до первой пустой строки в исходном файле.
В одном операторе можно ввести больше одного стоп-слова (когда это удобно), если записать их подряд следующим образом.
<source lang="perl">
print <<"END1".<<'END2';
double quotes
END1
single quotes
END2
</source>
По умолчанию, Here-docs сохраняет все лидирующие пробелы в многострочной строке, а также требует, чтобы стоп-слово всегда находилось в первой колонке. Например,
<source lang="perl">
print <<END;
ONE
TWO
THREE
END
</source>
напечатает
<pre>
ONE
TWO
THREE
</pre>
Такое поведение не всегда удобно, особенно, когда Here-docs вложена в некоторый блок: в этом случае в исходном коде нарушается выравнивание, из-за чего его неудобно читать. Сравните
<source lang="perl">
# Операция размещена в блоке, но мы не можем выровнять отступы как нам хочется, а также
# стоп-слово END должно всегда начинаться с первой колонки.
if (1) {
print <<END;
ONE
TWO
THREE
END
}
</source>
С версии 5.26 для Here-docs была добавлена особая форма (<code><<~</code>), которая вычисляет отступ для многострочной строки не от колонки документа с исходным кодом, а от отступа самого стоп слова. Таким образом, с версии 5.26 тот же фрагмент можно записать с сохранением исходных отступов так:
<source lang="perl">
# С версии 5.26
if (1) {
print <<~END;
ONE
TWO
THREE
END
# ^- отступ для многострочной строки вычисляется от этой колонки.
}
</source>
Операция Here-docs может комбинироваться с другими операциями. Для этого удобно пользоваться мнемоническим приемом, в котором нужно представлять, что очередная строка заменяет собой стоп-слово справа от знака <code><<</code>. Вот несколько примеров:
<source lang="perl">
# Каждая строка подается операции uc, которая переводит символы в верхний регистр.
print uc(<<EOF);
one
two
three
EOF
# Результат:
# ONE
# TWO
# THREE
# Удалит лидирующие пробелы для каждой строки.
print <<'EOF' =~ s/^\s+//mgr;
Line one with leading spaces
Line two with leading spaces
EOF
# Результат:
# Line one with leading spaces
# Line two with leading spaces
# При выводе строк будет приписывать к каждой порядковый номер.
sub numbering {
print ++$counter . ": " . $_ . "\n" for split '\n', $_[0];
}
numbering(<<'EOF');
int main(int argc, char** argv)
{
return 0;
}
EOF
# Результат:
# 1: int main(int argc, char** argv)
# 2: {
# 3: return 0;
# 4: }
# Напечатает между строками промежуточную.
print <<'PART1' . "Middle String\n" . <<'PART2';
This is the first part.
PART1
This is the final part.
PART2
# Результат:
# This is the first part.
# Middle String
# This is the final part.
</source>
== Другие операции ==
=== Диапазон ===
Бинарная операция диапазон <code>..</code> по-разному интерпретируется в скалярном и списковом контекстах.
В списковом контексте операция возвращает список из чисел, начиная с левого операнда и до последнего с шагом 1, если оба операнда являются числами, либо строки, которые будут сгенерированы в лексикографическом порядке. Если операндами являются числа, то левый операнд всегда должен быть меньше правого, иначе операция вернет пустой список. Числа также должны быть всегда целыми (если это не так, то дробная часть будет просто отбрасываться).
Если строка-левый операнд в лексикографическом смысле больше правого, то результат может зависеть от версии интерпретатора <code>perl</code>: в старых версиях по умолчанию просто возвращается левый операнд; в версии 5.32 будет возвращен ряд, начиная с большего операнда и до символа <code>z</code>. Кроме того, нельзя использовать символы из других алфавитов, кроме латинского: для других алфавитов всегда возвращается левый операнд как результат.
<source lang="perl">
(1..5); # 1 2 3 4 5
(-3..5); # -3 -2 -1 0 1 2 3 4 5
("a".."d"); # a b c d
("01".."10"); # 01 02 03 04 05 06 07 08 09 10
("a1".."b2"); # a1 a2 a3 ... a9 b1 b2
</source>
В скалярном контексте диапазон возвращает булево значение и эмулирует поведение операции запятая <code>,</code> из редакторов Sed и AWK (например, конструкция <code>1,3p</code>). Алгоритм поведения может показаться сложным при первом прочтении, тем не менее следует познакомиться с ним:
* Диапазон в скалярном контексте всегда находится в одном из двух состояний, которое может измениться при повторном вычислении. Условно мы назовем их ИСТИНА и ЛОЖЬ.
* Диапазон остается в ЛОЖНОМ состоянии до тех пор, пока ЛОЖНЫМ остается значение левого операнда. Как только он становится ИСТИНОЙ, диапазон переключает состояние в ИСТИНУ и остается в нем, пока правый операнд остается ИСТИННЫМ.
* Если диапазон находился в состоянии ИСТИНА и правый операнд стал ЛОЖНЫМ, то диапазон снова переключается в ЛОЖНОЕ состояние и вся предыдущая логика повторяется заново.
* В ЛОЖНОМ состоянии этот диапазон возвращает пустую строку (или ЛОЖЬ). В состоянии ИСТИНА при каждом повторном вычислении возвращается порядковый номер, начиная с 1.
* В момент перехода из ИСТИНЫ в ЛОЖЬ диапазон возвращает строку, в которой к последнему порядковому номеру добавляется <code>"E0"</code>, например <code>2E0</code>. В ИСТИННОМ состоянии <code>E0</code> будет отбрасываться.
Вот собственно и весь алгоритм. Добавление <code>"E0"</code> при смене состояния эмулирует поведение именно утилиты AWK. В Sed добавление этой строки происходит только на следующем вычислении. Чтобы эмулировать именно поведение Sed, нужно использовать три точки <code>...</code>.
Диапазоны в скалярном контексте в Perl обычно используются при чтении файлов.
=== Операция запятая ===
Операция запятая <code>,</code> является бинарной и ведет себя по-разному в скалярном и списковом контекстах:
* в списковом контексте она является разделителем для элементов в списке;
* в скалярном контексте она сначала вычисляет левый операнд, потом правый и возвращает результат правого операнда как общий результат.
<source lang="perl">
# Так как левый операнд присваивания это переменная на массив, то выражение исполняется в списковом контексте.
# В этом примере запятая разделяет элементы списка, который передается конструктору массива.
@arr = (1, 2);
# Теперь выражение выполняется в скалярном контексте. Оба операнда для запятой являются простыми числами, но
# как результат операции, будет возвращена 2, которая будет присвоена скаляру.
$scalar = (1, 2);
</source>
У запятой есть синоним в виде <code>=></code>, который мы ранее использовали для построения хеш-массивов в конструкторе. В основном этот знак повторяет запятую, но требует чтобы левый операнд всегда имел строковый тип и корректно в него конвертировался.
Операция является излюбленным инструментом трюков интерпретации. Например, следующая конструкция по порядку вызовет все процедуры, при этом в первой печати мы попутно захватываем результат вызова процедуры <code>b</code>, пользуясь тем, что операция используется в списковом контексте, в котором мы можем пользоваться индексацией массива, а во втором случае, выполняя все в скалярном контексте, мы получаем результат процедуры <code>c</code>:
<source lang="perl">
sub a { print "1\n"; "a" }
sub b { print "2\n"; "b" }
sub c { print "3\n"; "c" }
print STDOUT ( &a, &b, &c )[1], "\n";
print STDOUT scalar ( &a, &b, &c ), "\n";
</source>
<pre>
1
2
3
b
1
2
3
c
</pre>
=== Тернарная операция ===
В Perl есть тернарная условная операция, которая есть во многих языках программирования. Она записывается точно также как в Си:
<source lang="perl">
<операнд 1> ? <операнд 2> : <операнд 3>
</source>
Операция работает так. Если <code><операнд 1></code> является ИСТИНОЙ, то возвращается <code><операнд 2></code> как результат тернарной операции, иначе возвращается <code><операнд 3></code>. Результат тернарной операции можно использовать как в левосторонних (''lvalue''), так и в правосторонних выражениях (''rvalue''), главное чтобы они подставлялись в правильном семантическом контексте.
<source lang="perl">
$var = ($num % 3) ? ($var1 += 1) : ($var1 += 2);
(($a % 3) ? ($a += 2) : $a) -= 2; # Пример с lvalue выражением
</source>
=== Ссылочные операции ===
Большую часть времени мы ссылаемся на данные в памяти компьютера через переменные. Однако язык Perl, подобно Си, разрешает ссылаться на данные в памяти с помощью ''указателей'' или ''ссылок'' (в Perl эти термины равнозначны), которые хранят адрес на ячейку в памяти как значение. Ссылочная передача удобна, когда одни и те же данные разделяют множество процедур и при этом не нужно привязываться к конкретным именам или типам в тексте программы. Возможно процедура сама должна разобраться, что хранится в памяти по переданной ссылке и принять решение самостоятельно.
Чтобы получить адрес переменной, нужно перед ее именем написать обратный слеш <code>\</code> (это аналогично операции получения адреса <code>&</code> в языке Си).
<source lang="perl">
# Ссылка на скаляр
$var = 5;
$ptr = \$var; # Теперь здесь хранится адрес $var
# Ссылка на массив
@arr = (1,2,3);
$aptr = \@arr;
# Ссылка на хеш-массив
%hash = (name => "Larry", surname => "Wall");
$hptr = \%hash;
</source>
Обратите внимание, что адреса хранятся в скалярной переменной. Если мы распечатаем значения, то получим примерно следующее:
<source lang="">
SCALAR(0x800075938)
ARRAY(0x8000759b0)
HASH(0x800075aa0)
</source>
Обратите внимание, что в ссылке хранится информация о типе объекта, на который она ссылается и шестнадцатеричный адрес в памяти компьютера.
Чтобы выполнить обратную операцию, т.е. получить данные по ссылке, ее нужно ''разыменовать''. В отличие от Си, в Perl разыменовывание привязано к типу данных, на который она ссылается. Для этого нужно перед именем ссылки поставить тип, на который будет вестись разыменовывание.
<source lang="perl">
# Разыменовываем ссылку на хеш-массив, чтобы передать его функции keys, которая извлечет из него ключи
@keys = keys(%$hptr);
print $$ptr, "\n"; # Разыменовываем указатель на скаляр
</source>
В предыдущем примере с хеш-массивом мы сначала разыменовали ссылку на хеш-массив, указав символ <code>%</code> в начале, а только потом работали с массивом как обычно, передав его функции <code>keys</code>. Perl позволяет также обращаться к данным массивов непосредственно из ссылки (когда это удобно) при помощи операции разыменовывания массивов <code>-></code>.
<source lang="perl">
print $aptr->[0], $hptr->{name}, "\n";
</source>
Подробнее ссылки мы рассмотрим в отдельной главе (''см.'' [[{{Root book name}}/Ссылки|Ссылки]]).
=== Операции связывания ===
Регулярные выражения являются визитной карточкой Perl, который снабжен различными операциями для проверки по ним. Сюда входят поиск (<code>m//</code>), подстановка (<code>s///</code>) и замена (<code>tr///</code>). По умолчанию они работают с переменной <code>$_</code>.
Кроме них есть операции связывания <code>=~</code> и <code>!~</code>, которые выражают соответственно ''совпадает с шаблоном'' и ''не совпадает с шаблоном''.
Регулярные выражения являются очень обширной темой в Perl, поэтому они будут рассмотрены в отдельной главе (''см.'' [[{{Root book name}}/Работа со строками и регулярные выражения|Работа со строками и регулярные выражения]]).
==== Умное связывание ====
Начиная с 5.10.1 и до 5.37.10 в Perl поддерживалась операция умного связывания, которая умеет сравнивать переменные, хранящие разные типы данных. Данная бинарная операция обозначается двумя тильдами <code>~~</code> и изначально была введена для поддержки оператора <code>given..when</code> (в котором она используется неявно), о котором немного рассказано в следующей главе (см. [[Некоторые сведения о Perl 5/Операторы#Given..when|Операторы#Given..when]]).
Эта операция является особенной, так как её контекст определяется не на этапе компиляции, а во время исполнения программы. Для этого она определяет какой тип данных хранится в её операндах и, в зависимости от этих типов, выбирает механизм сравнивания, причем фактические типы в операндах не обязательно должны совпадать, что и делает её «умной». Если в понимании выбранного механизма операнды равны, она возвращает 1, иначе пустую строку <code>""</code>.
В Perl 5.37.10 данная операция была отмечена как неудачный эксперимент и окончательно будет вырезана в 5.42. Здесь мы о ней упоминаем, чтобы предостеречь не использовать её, хотя следует отметить сколько релизов ей удалось пережить.
=== Именованные унарные операции ===
В Perl есть большое число встроенных функций с одним аргументом, которые с точки зрения синтаксического анализатора являются ''унарными именованными операциями''<ref>Слово ''именованный'' подчеркивает тот факт, что у операции есть имя в виде обозначающего слова, при этом они не требуют скобок вокруг аргумента, копируя операции с одним символом.</ref>. Такие операции подобны функциям, но не требуют скобок вокруг параметра, как того требуют простые функции. О том, является ли функция такой операцией, можно узнать из документации. К таким операциям относятся например <code>do</code>, <code>eval</code>, <code>return</code>, <code>goto</code>, <code>sin</code>, <code>sqrt</code> и другие, а также все операции проверок файлов типа <code>-e</code> (файл существует), <code>-w</code> (в файл можно писать) и другие.
Ключевая особенность именованных унарных операций в Perl заключается в их приоритете: они имеют более высокий приоритет, чем большинство бинарных операций, но ниже, чем скобки и операция стрелки (<code>-></code>).
По ходу изложения мы затронем многие из этих операций.
=== Операции ввода/вывода ===
Это операции, которые направлены на вывод результатов в файлы или ввод данных в программу из них. Так, унарную именованную операцию <code>print</code> можно рассматривать как операцию вывода.
Чтобы обеспечить ввод данных в программу из файлов, нужно сначала их открыть и получить специальный программный объект, называемый ''дескриптором файла''. Дескриптор файла может быть получен через операцию в виде угловых скобок <code><дескриптор_файла></code>:
<source lang="perl">
open(DFILE, "net.cfg"); # Открываем файл. Идентификатор дескриптора DFILE может быть произвольным
@content = <DFILE>; # Читаем файл в массив
</source>
Если бы мы открыли файл на запись, то мы можем перенаправить запись в него так
<source lang="perl">
print MYFILE "Hello";
</source>
Тема ввода/вывода является очень обширной, поэтому она будет рассмотрена в отдельной главе (''см.'' [[{{Root book name}}/Ввод и вывод|Ввод и вывод]]).
== Выражения ==
Понимание того, как интерпретатор Perl парсит выражения, поможет вам лучше понять как они должны составляться. ''Выражение'' — это несколько операндов, объединенных одной и более операциями. Операнд в свою очередь также может быть выражением, т.е. одно общее выражение это конгломерат подвыражений.
Синтаксический анализатор Perl, во время разбора выражения, пытается выделить в нем элементарные члены — ''термы''<ref>Если делать сравнение с естественным языком, то терм является тем же самым, что и ''слово'' в предложении.</ref>, а потом соединить их знаками операций, причем он выстраивает операции в некотором порядке, учитывая приоритет.
В Perl термами являются литералы, переменные, выражения в скобках, закавыченная строка символов, а также функции с параметрами в скобках. Только терм может быть операндом. Отметим, что в Perl отсутствуют истинные функции: по существу они являются списковыми, унарными именованными операциями, ведущими себя как функции. Вызывая функцию, мы выполняем списковую или унарную операцию, которая возвращает некоторый результат.
<source lang="perl">
# Следующее выражение демонстрирует списковую операцию. В ней сначала
# вычисляется как терм каждый элемент списка, а затем каждый элемент выводится
# на печать списковой операции.
print 2 + 2 * 25 / 4, ' ', 16; # Напечатает '14.5 16'
# Следующее выражение дает аналогичный результат. Избыточные скобки
# порождают массив, передаваемый операции.
print (2 + 2 * 25 / 4, ' ', 16); # Напечатает '14.5 16'
# Теперь попробуем добавить конкатенацию:
print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 16'
# Обратите внимание, что результат конкатенации потерялся (нет символа '$'
# в выводе). Это произошло потому, что левый операнд конкатенации, как терм
# по приоритету вычисляется первым, т.е. print выводит результат своего терма
# в аргументе, а не результат конкатенации.
# Допустимо
print print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 161$'
# Появление единицы объясняется тем, что внутренний print вернул код 1, который
# был преобразован в строку и был объединен конкатенацией
# c '$'. Так как между внутренним и внешним print нет переноса
# строки, получилась такая склеенная строка.
</source>
При синтаксическом разборе выражений и операторов, как термы рассматриваются конструкции <code>do{}</code> и <code>eval{}</code>, вызовы подпрограмм и методов объектов, анонимные конструкторы массивов скаляров и хешей, а также все операции ввода/вывода.
Еще один немаловажный термин это ''контекст операции''. Операции и термы вычисляются в некотором контексте, от которого зависит поведение операции и возвращаемое ей значение. Существует два основных контекста: ''скалярный'' и ''списковый''. В скалярном контексте интерпретатор пытается вывести из терма скаляр, а в списковом контексте — список литералов <code>qw{}</code>. Контекст определяет обычно тип данных левого операнда. Так, если он является скаляром, то операция выполняется в скалярном контексте; если массив скаляров — списковом контексте. Некоторые операции способны распознавать в каком контексте они исполняются и возвращать свой результат в зависимости от него (универсальные операции).
<source lang="perl">
# В списковой операции print перед списком можно указать выходной дескриптор:
print STDOUT (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '16$'
# Потеря 14.5 Объясняется тем, что левый операнд конкатенации
# находится в скалярном контексте, а в нем операция запятая
# возвращает последний операнд как результат.
# Уберем скобки:
print STDOUT 2 + 2 * 25 / 4, ' ', 16 . '$'; # Напечатает '14.5 16$'
# Теперь каждый элемент списка после вычисления
# передается списковой операции, каждый в скалярном контексте.
</source>
Так как в Perl типизация динамическая<ref>Другими словами, все операции в Perl изначально перегружены.</ref>, понятие контекста операции является аналогом операции ''приведения типов данных'', которая есть в типизированных языках программирования. В большинстве ситуаций Perl знает лучше, в каком контексте вызывать операцию, но иногда приходится ему помогать, используя синтаксические конструкции и специальные функции. Умение предсказывать, в каком контексте выполнится операция в выражении, позволяет программисту Perl придумывать подчас очень запутанные (но с другой стороны короткие) конструкции, которые не могут себе позволить другие языки программирования из-за типизации. Мы рассмотрим некоторые случаи ниже.
<source lang="perl">
# В простом случае левый операнд задает контекст операции.
@arr = ("apple", "pear", "grapes", "banana");
# Левый операнд скаляр, а значит присваивание работает в скалярном контексте.
# В скалярном контексте массивы в правом операнде приводятся к их размеру.
$scalar = @arr;
print "$scalar\n";
# Левый операнд список скаляров, а значит присваивание работает в списковом контексте.
# В списковом контексте присваивание будет копировать элементы из массива в правом операнде
# поэлементно в список в левом операнде. При этом список слева не должен превышать размера массива.
( $element1, $element2, $element3 ) = @arr;
print $element1
. " $element2"
. " $element3\n";
# Если слева будет переменная на массив, операция также будет в списковом контексте,
# и правый массив переопределит значение переменной.
@arr1 = (1..4);
@arr1 = @arr;
print "@arr1\n";
#-------------------------------------------------------------------------------------------------------
# Операция print всегда работает в списковом контексте и все элементы списка пытается интерпретировать
# как строки.
# В стандартной библиотеке Perl есть функция localtime, которая в списковом контексте возвращает
# список из 9 элементов, кодирующих текущую дату.
print localtime; print "\n";
# Выведет примерно такое: 3219121301251120
# Однако, localtime умеет работать в скалярном контексте, в котором выводит дату в формате текущей локали,
# но этот контекст не может использоваться по умолчанию, так как компилятор только знает о том, что
# print умеет работать в списковом контексте.
#
# Этот тот случай, когда мы можем подсказать компилятору выполнить localtime в скалярном контексте,
# чтобы напечатать дату в читаемой форме. Для этого нужно воспользоваться функцией scalar, которая
# принуждает интерпретировать выражение в своем аргументе исключительно в скалярном контексте.
print scalar localtime; print "\n";
# В локали LANG_EN будет печататься в формате "Mon Jan 13 12:32:17 2025".
# Можно поступить проще и привести компилятор к скалярному контексту через другую операцию, которая
# этот контекст использует. Например, операция конкатенации строк всегда работает в скалярном контексте.
# Если компилятор увидит выражение, использующее скалярный контекст, он будет использоваться для всех
# операндов операции. Таким образом, такого же результата мы добиваемся, если напишем так:
print localtime . "\n";
# "Mon Jan 13 12:32:17 2025"
#-------------------------------------------------------------------------------------------------------
# Рассмотрим более сложный пример.
# Пусть дана такая строка:
$word = "111example222";
# Вопрос: как посчитать в ней количество символов цифр и букв и совместить это с печатью в одном компактном выражении?
# Для ответа на вопрос "сколько символов данного класса?" здесь следует использовать регулярные выражения.
# Так как о регулярных выражениях мы будем говорить много позже, возьмите на веру, что регулярное выражение для
# поиска цифр выглядит так '/\d/g', а букв так '/[^\d\s]/g'.
#
# Примечание: '/[^\d\s]/g' не учитывает Unicode символы.
#
# Проблема кроется в том, что регулярные выражения умеют работать и в скалярном, и в списковом контекстах.
# Конкретно нам нужен списковый контекст, в котором операция возвращает массив совпадений по шаблону, который
# мы могли бы преобразовать в размер, тем самым ответив на вопрос "сколько?". Но как нам сказать, чтобы операция
# выполнялась в списковом контексте, а не скалярном, ведь специальной функции типа scalar для этого нет?
#
# Единственный способ сделать это, привести компилятор к этому контексту через другую операцию.
# Существует два распространенных приёма провоцировать списковый контекст:
# * Можно использовать операцию присваивания, в которой левый операнд это пустой массив.
# * Можно использовать операцию разыменовывания ссылки, которая будет работать в контексте типа
# разыменовывания.
# Ниже мы в одном выражении используем оба приема для демонстрационных целей
print "Number of letters: "
. ( () = $word =~ /[a-zA-Z]/g ) . "\n"
. "Number of digits: "
. ( @{ [ $word =~ /\d/g ] } ) . "\n"
;
# Number of letters: 7
# Number of digits: 6
</source>
Не расстраивайтесь, если вы ничего из предыдущих примеров не поняли, так как мы сильно забегаем в них вперед. Эти примеры призваны показать, как сильно выражения завязаны на контекст операции.
Скалярный контекст также можно разделить по типу хранимых данных на ''числовой'', ''строковый'' и ''безразличный''. Perl неспособен заранее распознать этот контекст, поэтому он просто подключает механизм неявного приведения данных, когда скаляры в операндах хранят разные типы.
Еще один специальный скалярный контекст — ''булевый''. Так как в Perl нет булева <code>true</code> или <code>false</code>, Perl анализирует строки и числа в булевом контексте, как это было описано выше: <code>0</code>, <code>"0"</code>, <code>"00"</code>, <code>"0.0"</code> и <code>""</code> это условно ЛОЖЬ, а все остальное ИСТИНА.
Еще одним контекстом является void-контекст, который используется, когда в выражении нет побочных эффектов. Так, вы можете спокойно писать в Perl такие бессмысленные конструкции (они выполняются в void-контексте):
<source lang="perl">
(1,2,3);
$n;
"Hello";
</source>
Этот контекст можно обнаружить, если интерпретатор Perl запущен в режиме вывода предупреждающих сообщений.
Еще один контекст это ''подстановочный контекст'', в котором вычисляются операции заключения в кавычки (кроме одинарных кавычек). В этом контексте подставляются переменные и интерпретируются управляющие последовательности.
Ниже для справки представлены приоритеты операций.
{|class="wikitable" style="width:100%;"
|-
! Приоритет
! Операция
! Сочетаемость
|-
|style="width:10%;"|1
|Вычисление термов и левосторонних списковых операций
|style="width:20%;"|Слева направо
|-
|2
|<code><nowiki>-></nowiki></code>
|Слева направо
|-
|3
|<code><nowiki>++</nowiki></code> <code><nowiki>--</nowiki></code>
|Не сочетаются
|-
|4
|<code><nowiki>**</nowiki></code>
|Справа налево
|-
|5
|<code><nowiki>!</nowiki></code> <code><nowiki>~</nowiki></code> <code><nowiki>\</nowiki></code> унарный <code><nowiki>+</nowiki></code> унарный <code><nowiki>-</nowiki></code>
|Справа налево
|-
|6
|<code><nowiki>=~</nowiki></code> <code><nowiki>!=</nowiki></code>
|Слева направо
|-
|7
|<code><nowiki>*</nowiki></code> <code><nowiki>/</nowiki></code> <code><nowiki>%</nowiki></code> <code><nowiki>x</nowiki></code>
|Слева направо
|-
|8
|<code><nowiki>+</nowiki></code> <code><nowiki>-</nowiki></code> <code><nowiki>.</nowiki></code>
|Слева направо
|-
|9
|<code><nowiki><<</nowiki></code> <code><nowiki>>></nowiki></code>
|Слева направо
|-
|10
|Именованные унарные операции
|Не сочетаются
|-
|11
|<code><nowiki><</nowiki></code> <code><nowiki>></nowiki></code> <code><nowiki><=</nowiki></code> <code><nowiki>>=</nowiki></code> <code><nowiki>lt</nowiki></code> <code><nowiki>gt</nowiki></code> <code><nowiki>le</nowiki></code> <code><nowiki>ge</nowiki></code>
|Не сочетаются
|-
|12
|<code><nowiki>==</nowiki></code> <code><nowiki>!=</nowiki></code> <code><nowiki><=></nowiki></code> <code><nowiki>eq</nowiki></code> <code><nowiki>ne</nowiki></code> <code><nowiki>cmp</nowiki></code>
|Не сочетаются
|-
|13
|<code><nowiki>&</nowiki></code>
|Слева направо
|-
|14
|<code><nowiki>|</nowiki></code> <code><nowiki>^</nowiki></code>
|Слева направо
|-
|15
|<code><nowiki>&&</nowiki></code>
|Слева направо
|-
|16
|<code><nowiki>||</nowiki></code>
|Слева направо
|-
|17
|<code><nowiki>..</nowiki></code> <code><nowiki>...</nowiki></code>
|Не сочетаются
|-
|18
|<code><nowiki>?:</nowiki></code>
|Справа налево
|-
|19
|<code><nowiki>=</nowiki></code> <code><nowiki>**=</nowiki></code> <code><nowiki>+=</nowiki></code> <code><nowiki>-=</nowiki></code> <code><nowiki>.=</nowiki></code> <code><nowiki>*=</nowiki></code> <code><nowiki>/=</nowiki></code> <code><nowiki>%=</nowiki></code> <code><nowiki>x=</nowiki></code> <code><nowiki>&=</nowiki></code> <code><nowiki>|=</nowiki></code> <code><nowiki>^=</nowiki></code> <code><nowiki><<=</nowiki></code> <code><nowiki>>>=</nowiki></code> <code><nowiki>&&=</nowiki></code> <code><nowiki>||=</nowiki></code>
|Справа налево
|-
|20
|<code><nowiki>,</nowiki></code> <code><nowiki>=></nowiki></code>
|Слева направо
|-
|21
|Правосторонние списковые операции
|Не сочетаются
|-
|22
|<code><nowiki>not</nowiki></code>
|Справа налево
|-
|23
|<code><nowiki>and</nowiki></code>
|Слева направо
|-
|24
|<code><nowiki>or</nowiki></code> <code><nowiki>xor</nowiki></code>
|Слева направо
|}
* В данной таблице чем выше приоритет операции и чем левее она стоит (в большинстве случаев), тем раньше она выполняется.
* Приоритет списковой операции зависит от ее места в выражении и может изменяться от наивысшего к наименьшему. Все операции в выражении, стоящие слева от списковой, имеют наивысший приоритет по отношению к ней (смотрите 21 приоритет).
* Свойство сочетаемости означает, что операции одного приоритета могут быть в одном выражении, причем порядок их разрешения может быть слева направо или справа налево.
* Скобки позволяют изменить порядок вычисления, определяемому в обычных условиях приоритетом и сочетаемостью. Скобки всегда наделяют выражение в них наивысшим приоритетом.
== Примечания ==
{{примечания}}
suem3vxj9rzh3x9wc7y4lkl92h6y36m
269931
269930
2026-09-02T19:23:50Z
ScriptMaster
15708
/* Булевы выражения */
269931
wikitext
text/x-wiki
{{Навигация учебника}}
''Операция'' — это некоторые действия над операндами, результатом которых является новое значение. В Perl есть десятки операций, большинство из которых мы рассмотрим в этой главе, а другие в последующих. Операции можно делить по типу выполняемых ими действий (например, арифметические, логические и т.д.), по объекту, на который они воздействуют (строковые, списковые, файловые), а иной раз сложно отнести их к какой-то категории.
Большинство операций работает с двумя операндами, которые записываются по обе стороны от знака операции (''бинарная операция''). Например в операции сложения операндами выступают слагаемые. Для других операций может требоваться один операнд (''унарная операция'') или три (''тернарная операция''). В некоторых операциях какие-то операнды можно опускать.
Серия операций, записанная единым куском образует ''выражение''. Любое выражение интерпретируется слева направо, при этом порядок исполнения операций внутри выражения зависит от приоритета операций. Приоритеты операций в выражении можно менять через круглые скобки, которые дают более высокий приоритет.
== Арифметические операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Арифметические операции'''</center>
|-
|<center><code>+</code></center>
|style="width:10%;"|<center><code>6.35+8.31</code></center>
|style="width:30%;"|'''Сложение'''. Выполняет арифметическое сложение
|<center><code>-</code></center>
|style="width:10%;"|<center><code>9.15-5</code></center>
|style="width:30%;"|'''Вычитание'''. Выполняет арифметическое вычитание
|-
|<center><code>*</code></center>
|<center><code>2*2</code></center>
|'''Умножение'''
|<center><code>/</code></center>
|<center><code>15/3</code></center>
|'''Деление'''
|-
|<center><code>**</code></center>
|<center><code>4**2</code></center>
|'''Возведение в степень'''
|<center><code>%</code></center>
|<center><code>10%5</code></center>
|'''Остаток от деления'''
|-
| colspan="6" | <center>'''Унарные арифметические операции'''</center>
|-
|<center><code>+</code></center>
|<center><code>+5</code></center>
|'''Унарный плюс'''
|<center><code>-</code></center>
|<center><code>-6</code></center>
|'''Унарный минус'''. Делает положительное число отрицательным, а отрицательное — положительным
|-
| colspan="6" | <center>'''Инкремент/декремент'''</center>
|-
|<center><code>++</code></center>
|<center><code>$i = 5, $j=$i++;</code></center>
|'''Постфиксный инкремент'''. Увеличивает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 6, потому что инкремент выполняется после присваивания
|<center><code>++</code></center>
|<center><code>$i = 5, $j=++$i;</code></center>
|'''Префиксный инкремент'''. В отличие от постфиксного инкремента, значение в операнде будет увеличено до всех прочих операций над ним. В данном примере в обеих переменных будет 6
|-
|<center><code>--</code></center>
|<center><code>$i = 5, $j=$i--;</code></center>
|'''Постфиксный декремент'''. Уменьшает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 4, потому что декремент выполняется после присваивания
|<center><code>--</code></center>
|<center><code>$i = 5, $j=--$i;</code></center>
|'''Префиксный декремент'''. В отличие от постфиксного декремента, значение в операнде будет уменьшено до всех прочих операций над ним. В данном примере в обеих переменных будет 4
|}
* В основном арифметические операции направлены на работу с числами, однако операндами могут выступать и строки, причем не обязательно, чтобы они конвертировались в числа. В таких случаях интерпретатор сначала попытается выделить число из символов, начиная с первого. Если выделить число не получается, то результатом конвертации будет 0. Так, строка <code>5gh4</code> будет конвертирована в 5, а <code>g130</code> — в 0.
* Если в операции остаток от деления (<code>%</code>) второй операнд является вещественным числом и имеет дробную часть, то он предварительно преобразуется в целое число путем простого отброса дробной части.
* Отрицательное число <u>нельзя</u> возводить не в целую степень. Интерпретатор не будет выдавать никакой ошибки, но при этом результат всегда будет 0.
* Бинарные арифметические операции выполняются в ''скалярном контексте''. Это означает, что интерпретатор будет ожидать в операндах скалярные переменные. Если в операндах будут массивы, то они будут преобразованы к числу элементов в массиве, а в случае хеш-массива — к числу использованных хеш-записей в нем.
* Инкремент и декремент можно применять к строковым данным. В случае инкремента, если строка состоит только из латинских букв, то результатом будет новая строка, в которой последняя буква будет заменена на следующую по порядку в алфавите, причем строчная буква заменяется строчной, а прописная — прописной. Аналогично происходит, если строка завершается цифрой. Для декремента Perl будет пытаться конвертировать строку в число по обычным правилам, описанным выше.
== Строковые операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Строковые операции'''</center>
|-
|<center><code>.</code></center>
|style="width:10%;"|<center><code>"a"."b"</code></center>
|style="width:30%;"|'''Конкатенация строк'''. Объединяет две строки в одну
|<center><code>x</code></center>
|style="width:10%;"|<center><code>"a" x 3</code></center>
|style="width:30%;"|'''Повторитель'''. Повторяет строку в левом операнде N число раз и возвращает из этого строку
|}
* Строковые операции обычно направлены на строковый тип данных, однако можно использовать и числа. В этом случае числа будут преобразованы в строки.
* Оператор конкатенации не вставляет пробелы между строками. Обычно для объединения с пробелами прибегают к функции <code>join</code>.
* Для операции ''повторения'' левый операнд всегда приводится к строковому значению. Правым операндом должно быть целое число, либо строка, которая может быть приведена к целому числу. В случае вещественного числа, дробная часть будет просто отбрасываться.
* Для операции ''повторения'' левым операндом может быть список слов, заключенный в круглые скобки, т.е. эта операция может работать в ''списковом контексте''. В таком случае список будет повторен указанное число раз. Такой прием используется для порождения списка, передаваемого конструктору массива.
* Знак операции повторения следует всегда отделять хотя бы одним пробелом от его операндов, так как в некоторых ситуациях интерпретатор может принять его за часть имени переменной.
* С операцией повторения связана устойчивая идиома, используемая для быстрого заполнения одинаковыми значениями:<source lang="perl">
@arr[1..5] = (1) x 5;
print "@arr\n";
# 1 1 1 1 1
@hash{qw{ apples bananas pears }} = (0) x 3;
print "@{[%hash]}\n";
# apples 0 bananas 0 pears 0
</source>Если в левом операнде операции повторения используется массив, то операция будет выполняться в списковом контексте, в котором она повторит элементы указанное число раз в новом массиве. Если этот массив присвоить срезу массива или хэша (слева от присваивания), то сгенерированные повторением элементы будут присвоены элементам среза. На практике часто используется прием получения хеша из параметров командной строки, переданных сценарию:<source lang="perl">
my %exists;
@exists{@ARGV} = (1) x @ARGV;
# \ \____ В скалярном контексте развернется в число элементов массива @ARGV.
# \___________________ Получается хеш, в котором ключами будут элементы командной строки.
</source>
== Логические операции ==
=== Операции отношения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Числовые отношения'''</center>
|-
|<center><code>></code></center>
|style="width:10%;"|<center><code>2>1</code></center>
|style="width:30%;"|'''Больше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>>=</code></center>
|style="width:10%;"|<center><code>3>=1</code></center>
|style="width:30%;"|'''Больше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><</code></center>
|<center><code>-3<0</code></center>
|'''Меньше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code><=</code></center>
|<center><code>0<=3</code></center>
|'''Меньше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>==</code></center>
|<center><code>3==3</code></center>
|'''Равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>!=</code></center>
|<center><code>1!=2</code></center>
|'''Не равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><=></code></center>
|<center><code>15<=>63</code></center>
|'''Один из операндов больше другого'''. Возвращает -1, если правый операнд больше левого; 1 — правый операнд меньше левого; 0 — операнды равны
|
|
|
|-
| colspan="6" | <center>'''Строковые отношения'''</center>
|-
|<center><code>eq</code></center>
|<center><code>"a" eq "a"</code></center>
|'''Строки лексикографически равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ne</code></center>
|<center><code>"a" ne "b"</code></center>
|'''Строки лексикографически не равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>lt</code></center>
|<center><code>"a" lt "c"</code></center>
|'''Левая строка лексикографически меньше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>le</code></center>
|<center><code>"a" le "c"</code></center>
|'''Левая строка лексикографически меньше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>gt</code></center>
|<center><code>"d" gt "e"</code></center>
|'''Левая строка лексикографически больше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ge</code></center>
|<center><code>"d" ge "e"</code></center>
|'''Левая строка лексикографически больше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>cmp</code></center>
|<center><code>"a" cmp "b"</code></center>
|'''Лексикографически один из операндов не равен другому'''. Возвращает -1, если правый операнд лексикографически больше левого; 1 — правый операнд лексикографически меньше левого; 0 — операнды лексикографически равны
|
|
|
|}
* Обратите внимание, что операции отношения скопированы с командной оболочки Bourne Shell, но там все было наоборот: <code>eq</code>, <code>ne</code>, <code>lt</code>, <code>le</code>, <code>gt</code> и <code>ge</code> применялись к числам, а привычные нам знаки использовались для лексикографического сравнивания. Об этом нужно помнить, когда вы переключаетесь с программирования для командной оболочки на программирование для Perl.
* Лексикографическое сравнивание означает, что сравниваются ASCII кодировки символов. Так символ <code>a</code> лексикографически будет меньше символа <code>b</code>, потому что код последнего (98) больше первого (97). При лексикографическом сравнивании строк учитывается код каждого символа в строке, а также длина строк.
* ИСТИНА для числовых отношений это число 1, а для строковых — строка <code>"1"</code>.
* В общем случае следует быть осторожным при сравнивании вещественных чисел с целыми, так как тут вступает фактор округления чисел. Следует отметить, что вещественные числа вообще нельзя сравнивать на равенство, так как результат практически всегда будет неправильным. В этом случае следует сравнивать разницу двух вещественных чисел с очень малым числом (погрешностью). Можно использовать и другой приём, когда числа преобразуются в строки и сравниваются как строки.
=== Булевы выражения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Булевы операции'''</center>
|-
|<center><code>&&</code></center>
|style="width:10%;"|<center><code>1 && 1</code></center>
|style="width:30%;"|'''Конъюнкция'''
|<center><code><nowiki>||</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>0 || 1</nowiki></code></center>
|style="width:30%;"|'''Дизъюнкция'''
|-
|<center><code>!</code></center>
|<center><code>! 0</code></center>
|'''Логическое отрицание'''
|<center><code>xor</code></center>
|<center><code>1 xor 1</code></center>
|'''Исключающее ИЛИ'''
|-
|<center><code>and</code></center>
|<center><code>1 and 1</code></center>
|'''Конъюнкция'''. ''Синоним''
|<center><code>or</code></center>
|<center><code>0 or 1</code></center>
|'''Дизъюнкция'''. ''Синоним''
|-
|<center><code>not</code></center>
|<center><code>not 1</code></center>
|'''Логическое отрицание'''. ''Синоним''
|<center><code>^^</code></center>
|<center><code>1 ^^ 1</code></center>
|'''Исключающее ИЛИ'''<ref>Знак операции появился с версии интерпретатора 5.40.</ref>
|-
|<center><code>//</code></center>
|<center><code>$a // $b</code></center>
|'''Слияние'''
|
|
|
|}
* Операции <code>or</code>, <code>and</code>, <code>not</code> и <code>xor</code> появились в Perl, начиная с версии 5.001. Отличие их от аналогичных операций в виде символов в том, что они имеют меньший приоритет при вычислении в сложных выражениях. Такой пониженный приоритет иногда требуется в выражениях по типу <code>$num != 0 or print "\$num == 0";</code>
* Для булевых операций ИСТИНОЙ признается любое скалярное значение не равное <code>0</code>, <code>"0"</code> и <code>""</code>, в противном случае это ЛОЖЬ. Значение <code>undef</code> также приравнивается ко ЛЖИ.
* Булевы операции конъюнкции и дизъюнкции вычисляются по укороченной схеме. Это означает, например, что если при конъюнкции ЛОЖЬ в некотором операнде встречается раньше, чем закончилось выражение, вычисление прекращается на этом операнде с общим результатом ЛОЖЬ.
* Хотя булевы операции рассчитаны в основном на то, чтобы использовать их с числами, некоторые из них можно использовать для работы со строками. Полезной здесь оказывается дизъюнкция, которая может быть использована для записи укороченного присваивания значений по умолчанию, что показано в следующем примере: <source lang="perl">
# Эта процедура просто возвращает строку, переданную в аргументе, но если он не передается,
# то она назначит ему значение по умолчанию.
sub procedure {
my ($arg) = @_;
$arg ||= "default_value";
return $arg;
}
# Вызов без аргумента
print "Result: " . procedure . "\n";
# Result: default_value
# Вызов с аргументом
print "Result: " . procedure "some_value" . "\n";
# Result: some_value
</source> Также, в случаях, когда неопределенные значения недопустимы, она может использоваться для выбора из альтернативы: <source lang="perl">
$var = undef;
$var = $var || "default_value";
print "$var\n";
# default_value
# Размер цепочки может быть сколько угодно большим. В данном примере мы использовали
# undef для демонстрации, но представьте на их местах переменные. Значение по умолчанию
# выбирается с разным приоритетом (первое не пустое с левого края).
$value = undef || undef || undef || "default_value";
print "$value\n";
</source>
* Операция слияния (<code>//</code>) работает так: если левый операнд определен (''defined''), то вычисляется выражение, записанное в первом операнде и его результат возвращается как результат всей операции, иначе, если левый операнд не определен, но определен второй операнд, то его результат возвращается как результат всей операции. Если не определены оба операнда, то возвращается <code>undef</code> как результат всей операции. Технически операция похожа на тернарную операцию (см. ниже), но нацелена она на проверку условия ''определен'' / ''не определен''. Операция имеет пониженный приоритет, поэтому в сложных выражениях её обычно следует оставлять в скобках. Вот примеры, когда она может использоваться:<source lang="perl">
# Взять значение в кеше или вызвать процедуру по извлечению значения из хранилища.
$data = $cache{ $key } // get_from_storage($key);
# ----------------------------------------------------------
$x = 10;
$y = undef;
$z = 5;
# Это выражение ...
$result = $x + ($y // $z);
# ... является коротким способом записать такое
unless (defined $y) {
$result = $x + $z;
} else {
$result = $x + $y;
}
# ----------------------------------------------------------
$discounted_price = 60;
$base_price = 100;
$tax_rate = undef;
$price = $discounted_price // $base_price;
# Если налоговая ставка не определена, то она равняется нулю.
$final = $price * (1 - ($tax_rate // 0));
# ----------------------------------------------------------
$default_config = 'default';
# Будет присвоено первое определенное значение.
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# ----------------------------------------------------------
$default_config = undef;
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# Процедура compute_expensive_value будет вызвана только если config_value
# не определена.
$config_value //= compute_expensive_value();
sub compute_expensive_value {
'computed'
}
print "$config_value\n";
</source>
=== Побитовые операции ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Побитовые операции'''</center>
|-
|<center><code><nowiki>&</nowiki></code></center>
|style="width:10%;"|<center><code>3 & 2</code></center>
|style="width:30%;"|'''Побитовое И'''. Выполняет конъюнкцию двух чисел побитово
|<center><code><nowiki>|</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>3 | 2</nowiki></code></center>
|style="width:30%;"|'''Побитовое ИЛИ'''. Выполняет дизъюнкцию двух чисел побитово
|-
|<center><code>^</code></center>
|<center><code>3 ^ 3</code></center>
|'''Побитовое исключающее ИЛИ'''
|<center><code>~</code></center>
|<center><code>~0</code></center>
|'''Побитовое отрицание'''
|-
|<center><code><<</code></center>
|<center><code>1 << 1</code></center>
|'''Побитовый сдвиг влево'''
|<center><code>>></code></center>
|<center><code>2 >> 1</code></center>
|'''Побитовый сдвиг вправо'''
|}
* Побитовые операции работают только с числами, если хотя бы один операнд является числом (соответственно другой будет сконвертирован в число). Если '''оба''' операнда являются строками, то побитово выполняются операции с кодом каждого символа, даже если строки записаны только цифрами. Если при этом строки разной длины, то меньшая из них дополняется символами <code>\000</code> до равенства.
* Все вещественные числа преобразуются к целым.
* Допустимо использовать любой формат представления чисел: десятичный, шестнадцатеричный и восьмеричный. Язык Perl представляет числа с длиной 32 бит для поразрядных операций, независимо от архитектуры компьютера.
== Операции присваивания ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Операции присваивания'''</center>
|-
|<center><code>=</code></center>
|style="width:10%;"|<center><code>$i = 2</code></center>
|style="width:30%;"|'''Простое присваивание'''
|<center><code>**=</code></center>
|style="width:10%;"|<center><code>$i **= 2</code></center>
|style="width:30%;"|'''Возведение в степень с присваиванием'''
|-
|<center><code>+=</code></center>
|<center><code>$i += 2</code></center>
|'''Сложение с присваиванием'''
|<center><code>-=</code></center>
|<center><code>$i -= 2</code></center>
|'''Вычитание с присваиванием'''
|-
|<center><code>.=</code></center>
|<center><code>$i .= 2</code></center>
|'''Конкатенация с присваиванием'''
|<center><code>*=</code></center>
|<center><code>$i *= 2</code></center>
|'''Умножение с присваиванием'''
|-
|<center><code>/=</code></center>
|<center><code>$i /= 2</code></center>
|'''Деление с присваиванием'''
|<center><code>%=</code></center>
|<center><code>$i %= 2</code></center>
|'''Остаток от деления с присваиванием'''
|-
|<center><code>x=</code></center>
|<center><code>$i x= 2</code></center>
|'''Повторение с присваиванием'''
|<center><code>&=</code></center>
|<center><code>$i &= 2</code></center>
|'''Поразрядное И с присваиванием'''
|-
|<center><code><nowiki>|=</nowiki></code></center>
|<center><code><nowiki>$i |= 2</nowiki></code></center>
|'''Поразрядное ИЛИ с присваиванием'''
|<center><code>^=</code></center>
|<center><code>$i ^= 2</code></center>
|'''Поразрядное исключающее ИЛИ с присваиванием'''
|-
|<center><code><<=</code></center>
|<center><code>$i <<= 2</code></center>
|'''Поразрядный сдвиг влево с присваиванием'''
|<center><code>>>=</code></center>
|<center><code>$i >>= 2</code></center>
|'''Поразрядный сдвиг вправо с присваиванием'''
|-
|<center><code>&&=</code></center>
|<center><code>$i &&= 2</code></center>
|'''Конъюнкция с присваиванием'''
|<center><code><nowiki>||=</nowiki></code></center>
|<center><code><nowiki>$i ||= 2</nowiki></code></center>
|'''Дизъюнкция с присваиванием'''
|-
|<center><code>^^=</code></center>
|<center><code>$i ^^= 1</code></center>
|'''Исключающее ИЛИ с присваиванием'''<ref>Поддерживается с версии 5.42.</ref>
|<center><code>//=</code></center>
|<center><code>$value //= $default</code></center>
|'''Слияние с присваиванием'''
|}
* Простое присваивание является бинарной операцией, правый операнд которой является выражением, а левый — определяет участок памяти (в форме переменной), в который запишется результат выражения. Операция присваивания возвращает левый операнд как результат выражения. По этой причине следующая запись будет синтаксически правильной<source lang="perl">
($i = $j) = 5;
# Если развернуть эту конструкцию, то получается
$i = $j;
$i = 5; # Так как выражение в скобках вернет $i
</source>
* Как в языке Си, в Perl есть составное присваивание, совмещающее в себе операцию и присваивание. Операции с присваиванием также возвращают левый операнд, как результат выражения<source lang="perl">
$b = 1;
$a = ($b += 3); # Аналогично $a = $b = 4
$a += ($b += 3); # Аналогично $a = $a + $b + 3
(($a += 2) **= 2 ) -= 1; # Аналогично $a = ($a + 2) ** 2 - 1
</source>Обратим внимание, что в большинстве ситуаций в сложных выражениях ставить скобки для операций присваивания обязательно, потому что, как вы узнаете ниже, они парсятся интерпретатором справа налево.
== Списковые операции ==
''Списковая операция'' — это операция над списком значений. Операции, которым передаются списки и кажущиеся функциями, на самом деле являются списковыми операциями, например <code>print</code>. Это связано с тем, что списковые операции разрешают помещать свои аргументы в скобки. Так, следующие вызовы равнозначны
<source lang="perl">
print $a, "hello", $b;
print ($a, "hello", $b);
</source>
Списковые операции ожидают на входе список скаляров в некоторой форме, а на выходе отдают тот же список, но модифицированный некоторым образом, либо новый список. Так, следующие операции являются списковыми:
* Создание и конкатенация массивов<source lang="perl">
@array1 = (1,2,3);
@array2 = (4,5,6);
@array3 = (@array1, @array2);
</source>
* Встроенные операции, связанные с манипуляцией значением(ми) в массиве<source lang="perl">
@array = (1,2,3);
push @array, 4; # (1, 2, 3, 4)
pop @array; # (1, 2, 3); 4 возвращается как результат
shift @array; # (2, 3); 1 возвращается как результат
unshift @array, 5; # (5, 2, 3)
splice @array, 0, 2; # (3); (5, 2) возвращается как результат
</source>
* Трансформация (новый массив возвращается как результат)<source lang="perl">
@array = qw { a b c d e f };
# Применение предиката к каждому элементу в списке
print "$_\n" foreach map { $_ . $counter++ } @array;
#
# Вызов map в модификаторе foreach возвращает новый массив, каждый элемент которого
# печатается print слева от модификатора. В этом примере процедура предиката к каждому
# элементу исходного массива добавляет порядковый номер, в результате чего получается
#
# a0
# b1
# c2
# d3
# e4
# f5
#
# Сформировать новый массив, элементы которого хранятся в обратном порядке
print "$_\n" foreach reverse @array;
# f
# e
# d
# c
# b
# a
# Сортировка
use List::Util; # Подключаем модуль для перемешивания элементов для дальнейшего примера.
@array = List::Util::shuffle(@array); # Перемешиваем элементы в случайном порядке.
print "@{array}\n"; # Вывод будет отличаться при каждом вызове: "b f d a e c"
@array = sort @array; # Снова в алфавитном порядке.
print "@{array}\n"; # "a b c d e f"
</source>
* Фильтрующие операции (возвращают подмножество исходного массива как результат)<source lang="perl">
@array = qw { a 1 5 d 6 f 8 9 h j k l 25 };
@numbers = grep /\d+/, @array; # Достаем из исходного массива только цифры
print "@{numbers}\n"; # "1 5 6 8 9 25"
@letters = grep /[a-z]+/, @array; # Достаем из исходного массива только буквы
print "@{letters}\n"; # "a d f h j k l"
</source>
* Агрегирующие операции (как правило, возвращают все элементы массивов одним значением)<source lang="perl">
@words = qw { apple grapes banana };
$value = join ", ", @words; # объединяет все элементы и вставляет между ними разделитель.
print "${value}\n"; # "apple, grapes, banana"
</source>
* Разворачивающие операции (наоборот, из одного значения получают список)<source lang="perl">
@numbers = 1..5;
print "@{numbers}\n"; # "1 2 3 4 5"
$fruits = "apple, banana, grapes";
@fruits = split ", ", $fruits;
print "@{fruits}\n"; # "apple banana grapes"
</source>
Подробнее о пользовательских списковых операциях будет в главе, посвященной функциям.
=== Списковые операции all и any ===
Начиная с версии 5.42, в ядро интерпретатора были интегрированы две новые операции, помогающие работать с множествами без использования модулей. До этого эти операции приходилось подключать в виде функций из модуля <code>List::Util</code>. Вероятно, добавление этих операций в ядро было вдохновлено языком Python, в котором они существуют в виде операторов.
<source lang="perl">
any { <блок-предиката> } @list;
all { <блок-предиката> } @list;
</source>
Операции <code>all</code> и <code>any</code> мимикрируют под операцию <code>grep</code> и используются, чтобы вернуть логический ответ по предикату. Чтобы операция <code>all</code> вернула ИСТИНУ, необходимо чтобы её предикат вернул ИСТИНУ для каждого элемента списка. Соответственно, для операции <code>any</code> ИСТИНОЙ считается ситуация, когда предикат возвращает ИСТИНУ по меньшей мере для одного элемента списка.
Появление этих операций, хоть и с опозданием, очень кстати, так как различного рода проверки множеств — частая задача на практике. Вот лишь несколько примеров.
<source lang="perl">
use v5.42; # Директива требует интерпретатор не ниже указанной версии.
# Следующие директивы нужны по крайней мере для версии 5.42, так как на тот момент
# эти возможности были еще экспериментальными. В будущих версиях эти
# директивы не понадобятся.
use feature 'keyword_any', 'keyword_all'; # Включает операции
no warnings 'experimental::keyword_any', 'experimental::keyword_all'; # Отключает предупреждения
# Использование my в этом месте обусловлено тем, что директива use v5.42
# автоматически включает директиву strict (будет рассмотрено позже).
my @numbers = (2, 4, 6, 8, 10);
# Через специальную переменную $_ предикат получает очередной элемент списка.
if (all { $_ % 2 == 0 } @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Сравните этот код с программой, которую мы бы написали для версии интерпретатора меньше 5.42:
<source lang="perl">
use List::Util qw{ all any }; # Импортируем функции из модуля.
@numbers = (2, 4, 6, 8, 10);
# ____ Запятую можно опустить, так как у функции есть прототип. Для any она опущена.
# V
if (all { $_ % 2 == 0 }, @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Обратите внимание на то, что в старых версиях интерпретатора функции <code>all</code> и <code>any</code> всегда нужно импортировать из модуля <code>List::Util</code>. Однако не спешите полностью списывать этот модуль, так как в нём есть множество других полезных функций.
== Операции заключения в кавычки ==
Кавычки (одинарные, двойные и обратные) используются в Perl, чтобы задавать строковые литералы. Кроме этого есть еще операции, которые внешне похожи на операции заключения в кавычки, и перечислены в таблице ниже.
{|class="wikitable" style="width:100%;"
|-
! Операция
! Эквивалент
! Описание
! Операция
! Эквивалент
! Описание
|-
| colspan="6" | <center>'''Операции заключения в кавычки'''</center>
|-
|<center><code>q{}</code></center>
|style="width:10%;"|<center><code><nowiki>''</nowiki></code></center>
|style="width:30%;"|'''Строковый литерал'''. Подстановки невозможны
|<center><code>qq{}</code></center>
|style="width:10%;"|<center><code>""</code></center>
|style="width:30%;"|'''Строковый литерал'''
|-
|<center><code>qx{}</code></center>
|<center><code><nowiki>``</nowiki></code></center>
|'''Команда системы'''
|<center><code>qw{}</code></center>
|<center><code><nowiki>()</nowiki></code></center>
|'''Список слов'''. Подстановки невозможны
|-
|<center><code>m{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Поиск по регулярному выражению'''
|<center><code>qr{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Шаблон регулярного выражения'''
|-
|<center><code>s{}{}</code></center>
|<center><code>s///</code></center>
|'''Подстановка'''
|<center><code>tr{}{}</code></center>
|<center><code><nowiki>y///</nowiki></code></center>
|'''Транслитерация'''. Подстановки невозможны
|}
* Фигурные скобки в данных операциях представляют собой разделитель. Они могут быть в любой момент заменены на другой символ или любую другую пару скобок, например<source lang="perl">
q{hello}
q/hello/
q!hello!
q<\<hello\>> # Если символы разделителя есть в значении, то они должны экранироваться.
</source>Внутри разделителей разрешается любое число пробелов, а также внутри большинства операций разрешены подстановки, кроме некоторых (указанных в таблице отдельно).
* Операция <code>qw{}</code> позволяет вернуть список слов. Чаще всего она используется, чтобы инициализировать массив.<source lang="perl">
@arr = qw{ one two three };
# Это аналогично
@arr = ("one", "two", "three");
# и
$arr[0] = "one";
$arr[1] = "two";
$arr[2] = "three";
</source>Обратите внимание, что внутри <code>qw{}</code> запятые ставить не нужно: разделителями являются пробелы.
* Об остальных операциях будет рассказано далее по ходу изложения.
=== Операция Here-docs ===
В Perl перекочевала Here-docs операция, придуманная для командной оболочки Bourne Shell, которая позволяет инициализировать переменные многострочными строками, либо передавать их на вход некоторой операции, записывая строки прямо в исходном файле программы. Как и в Bourne Shell она записывается так
<source lang="perl">
$str = <<EOF;
строка 1
строка 2
строка 3
EOF
print <<END;
Hello, World!
END
</source>
Здесь после знака <code><<</code> вводится так называемое стоп-слово. Далее записывается многострочная строка. Интерпретатор будет искать стоп-слово, для того чтобы понять где заканчивается эта строка. Стоп-слово может быть любым строковым литералом. В наших примерах стоп-словами были <code>EOF</code> и <code>END</code>.
За стоп-словом обычно следует ставить символ точки с запятой, так как Here-docs является простым оператором, конец которого находится после ввода стоп-слова. Пропуск точки с запятой является очень частой ошибкой. Тем не менее, если оператор Here-docs является единственным или последним в текущем блоке, точку с запятой можно опускать.
По умолчанию многострочная строка записывается так, как будто она находится в двойных кавычках. Таким образом, в ней будут разрешаться все подстановки и управляющие последовательности. Вы можете явно обозначить, как должен поступать интерпретатор, если будете использовать соответствующие кавычки вокруг стоп-слова.
<source lang="perl">
$var = "Hello";
print <<END;
\t$var, World!
END
# Аналогично предыдущему вызову
print <<"END";
\t$var, World!
END
# С одинарными кавычками переменные перестанут подставляться и
# не будут работать управляющие последовательности
print <<'END';
\t$var, World!
END
</source>
Можно также использовать наклонные кавычки, тогда в Here-docs можно писать процедуры командной оболочки и инициализировать переменные результатом работы этих процедур. Каждая строка в таких процедурах это отдельный ввод команды в оболочку. Следует помнить, что результаты могут зависеть от операционной системы, в которой исполняется Perl.
<source lang="perl">
# Выведет каталоги корневой точки монтирования в *nix системе.
print <<`END`;
ls -l /
END
</source>
Если стоп-слово вводится без кавычек, то нельзя ставить пробелы между <code><<</code> и стоп-словом. Если такое случится, то Perl не распознает такого слова и будет захватывать строки до первой пустой строки в исходном файле.
В одном операторе можно ввести больше одного стоп-слова (когда это удобно), если записать их подряд следующим образом.
<source lang="perl">
print <<"END1".<<'END2';
double quotes
END1
single quotes
END2
</source>
По умолчанию, Here-docs сохраняет все лидирующие пробелы в многострочной строке, а также требует, чтобы стоп-слово всегда находилось в первой колонке. Например,
<source lang="perl">
print <<END;
ONE
TWO
THREE
END
</source>
напечатает
<pre>
ONE
TWO
THREE
</pre>
Такое поведение не всегда удобно, особенно, когда Here-docs вложена в некоторый блок: в этом случае в исходном коде нарушается выравнивание, из-за чего его неудобно читать. Сравните
<source lang="perl">
# Операция размещена в блоке, но мы не можем выровнять отступы как нам хочется, а также
# стоп-слово END должно всегда начинаться с первой колонки.
if (1) {
print <<END;
ONE
TWO
THREE
END
}
</source>
С версии 5.26 для Here-docs была добавлена особая форма (<code><<~</code>), которая вычисляет отступ для многострочной строки не от колонки документа с исходным кодом, а от отступа самого стоп слова. Таким образом, с версии 5.26 тот же фрагмент можно записать с сохранением исходных отступов так:
<source lang="perl">
# С версии 5.26
if (1) {
print <<~END;
ONE
TWO
THREE
END
# ^- отступ для многострочной строки вычисляется от этой колонки.
}
</source>
Операция Here-docs может комбинироваться с другими операциями. Для этого удобно пользоваться мнемоническим приемом, в котором нужно представлять, что очередная строка заменяет собой стоп-слово справа от знака <code><<</code>. Вот несколько примеров:
<source lang="perl">
# Каждая строка подается операции uc, которая переводит символы в верхний регистр.
print uc(<<EOF);
one
two
three
EOF
# Результат:
# ONE
# TWO
# THREE
# Удалит лидирующие пробелы для каждой строки.
print <<'EOF' =~ s/^\s+//mgr;
Line one with leading spaces
Line two with leading spaces
EOF
# Результат:
# Line one with leading spaces
# Line two with leading spaces
# При выводе строк будет приписывать к каждой порядковый номер.
sub numbering {
print ++$counter . ": " . $_ . "\n" for split '\n', $_[0];
}
numbering(<<'EOF');
int main(int argc, char** argv)
{
return 0;
}
EOF
# Результат:
# 1: int main(int argc, char** argv)
# 2: {
# 3: return 0;
# 4: }
# Напечатает между строками промежуточную.
print <<'PART1' . "Middle String\n" . <<'PART2';
This is the first part.
PART1
This is the final part.
PART2
# Результат:
# This is the first part.
# Middle String
# This is the final part.
</source>
== Другие операции ==
=== Диапазон ===
Бинарная операция диапазон <code>..</code> по-разному интерпретируется в скалярном и списковом контекстах.
В списковом контексте операция возвращает список из чисел, начиная с левого операнда и до последнего с шагом 1, если оба операнда являются числами, либо строки, которые будут сгенерированы в лексикографическом порядке. Если операндами являются числа, то левый операнд всегда должен быть меньше правого, иначе операция вернет пустой список. Числа также должны быть всегда целыми (если это не так, то дробная часть будет просто отбрасываться).
Если строка-левый операнд в лексикографическом смысле больше правого, то результат может зависеть от версии интерпретатора <code>perl</code>: в старых версиях по умолчанию просто возвращается левый операнд; в версии 5.32 будет возвращен ряд, начиная с большего операнда и до символа <code>z</code>. Кроме того, нельзя использовать символы из других алфавитов, кроме латинского: для других алфавитов всегда возвращается левый операнд как результат.
<source lang="perl">
(1..5); # 1 2 3 4 5
(-3..5); # -3 -2 -1 0 1 2 3 4 5
("a".."d"); # a b c d
("01".."10"); # 01 02 03 04 05 06 07 08 09 10
("a1".."b2"); # a1 a2 a3 ... a9 b1 b2
</source>
В скалярном контексте диапазон возвращает булево значение и эмулирует поведение операции запятая <code>,</code> из редакторов Sed и AWK (например, конструкция <code>1,3p</code>). Алгоритм поведения может показаться сложным при первом прочтении, тем не менее следует познакомиться с ним:
* Диапазон в скалярном контексте всегда находится в одном из двух состояний, которое может измениться при повторном вычислении. Условно мы назовем их ИСТИНА и ЛОЖЬ.
* Диапазон остается в ЛОЖНОМ состоянии до тех пор, пока ЛОЖНЫМ остается значение левого операнда. Как только он становится ИСТИНОЙ, диапазон переключает состояние в ИСТИНУ и остается в нем, пока правый операнд остается ИСТИННЫМ.
* Если диапазон находился в состоянии ИСТИНА и правый операнд стал ЛОЖНЫМ, то диапазон снова переключается в ЛОЖНОЕ состояние и вся предыдущая логика повторяется заново.
* В ЛОЖНОМ состоянии этот диапазон возвращает пустую строку (или ЛОЖЬ). В состоянии ИСТИНА при каждом повторном вычислении возвращается порядковый номер, начиная с 1.
* В момент перехода из ИСТИНЫ в ЛОЖЬ диапазон возвращает строку, в которой к последнему порядковому номеру добавляется <code>"E0"</code>, например <code>2E0</code>. В ИСТИННОМ состоянии <code>E0</code> будет отбрасываться.
Вот собственно и весь алгоритм. Добавление <code>"E0"</code> при смене состояния эмулирует поведение именно утилиты AWK. В Sed добавление этой строки происходит только на следующем вычислении. Чтобы эмулировать именно поведение Sed, нужно использовать три точки <code>...</code>.
Диапазоны в скалярном контексте в Perl обычно используются при чтении файлов.
=== Операция запятая ===
Операция запятая <code>,</code> является бинарной и ведет себя по-разному в скалярном и списковом контекстах:
* в списковом контексте она является разделителем для элементов в списке;
* в скалярном контексте она сначала вычисляет левый операнд, потом правый и возвращает результат правого операнда как общий результат.
<source lang="perl">
# Так как левый операнд присваивания это переменная на массив, то выражение исполняется в списковом контексте.
# В этом примере запятая разделяет элементы списка, который передается конструктору массива.
@arr = (1, 2);
# Теперь выражение выполняется в скалярном контексте. Оба операнда для запятой являются простыми числами, но
# как результат операции, будет возвращена 2, которая будет присвоена скаляру.
$scalar = (1, 2);
</source>
У запятой есть синоним в виде <code>=></code>, который мы ранее использовали для построения хеш-массивов в конструкторе. В основном этот знак повторяет запятую, но требует чтобы левый операнд всегда имел строковый тип и корректно в него конвертировался.
Операция является излюбленным инструментом трюков интерпретации. Например, следующая конструкция по порядку вызовет все процедуры, при этом в первой печати мы попутно захватываем результат вызова процедуры <code>b</code>, пользуясь тем, что операция используется в списковом контексте, в котором мы можем пользоваться индексацией массива, а во втором случае, выполняя все в скалярном контексте, мы получаем результат процедуры <code>c</code>:
<source lang="perl">
sub a { print "1\n"; "a" }
sub b { print "2\n"; "b" }
sub c { print "3\n"; "c" }
print STDOUT ( &a, &b, &c )[1], "\n";
print STDOUT scalar ( &a, &b, &c ), "\n";
</source>
<pre>
1
2
3
b
1
2
3
c
</pre>
=== Тернарная операция ===
В Perl есть тернарная условная операция, которая есть во многих языках программирования. Она записывается точно также как в Си:
<source lang="perl">
<операнд 1> ? <операнд 2> : <операнд 3>
</source>
Операция работает так. Если <code><операнд 1></code> является ИСТИНОЙ, то возвращается <code><операнд 2></code> как результат тернарной операции, иначе возвращается <code><операнд 3></code>. Результат тернарной операции можно использовать как в левосторонних (''lvalue''), так и в правосторонних выражениях (''rvalue''), главное чтобы они подставлялись в правильном семантическом контексте.
<source lang="perl">
$var = ($num % 3) ? ($var1 += 1) : ($var1 += 2);
(($a % 3) ? ($a += 2) : $a) -= 2; # Пример с lvalue выражением
</source>
=== Ссылочные операции ===
Большую часть времени мы ссылаемся на данные в памяти компьютера через переменные. Однако язык Perl, подобно Си, разрешает ссылаться на данные в памяти с помощью ''указателей'' или ''ссылок'' (в Perl эти термины равнозначны), которые хранят адрес на ячейку в памяти как значение. Ссылочная передача удобна, когда одни и те же данные разделяют множество процедур и при этом не нужно привязываться к конкретным именам или типам в тексте программы. Возможно процедура сама должна разобраться, что хранится в памяти по переданной ссылке и принять решение самостоятельно.
Чтобы получить адрес переменной, нужно перед ее именем написать обратный слеш <code>\</code> (это аналогично операции получения адреса <code>&</code> в языке Си).
<source lang="perl">
# Ссылка на скаляр
$var = 5;
$ptr = \$var; # Теперь здесь хранится адрес $var
# Ссылка на массив
@arr = (1,2,3);
$aptr = \@arr;
# Ссылка на хеш-массив
%hash = (name => "Larry", surname => "Wall");
$hptr = \%hash;
</source>
Обратите внимание, что адреса хранятся в скалярной переменной. Если мы распечатаем значения, то получим примерно следующее:
<source lang="">
SCALAR(0x800075938)
ARRAY(0x8000759b0)
HASH(0x800075aa0)
</source>
Обратите внимание, что в ссылке хранится информация о типе объекта, на который она ссылается и шестнадцатеричный адрес в памяти компьютера.
Чтобы выполнить обратную операцию, т.е. получить данные по ссылке, ее нужно ''разыменовать''. В отличие от Си, в Perl разыменовывание привязано к типу данных, на который она ссылается. Для этого нужно перед именем ссылки поставить тип, на который будет вестись разыменовывание.
<source lang="perl">
# Разыменовываем ссылку на хеш-массив, чтобы передать его функции keys, которая извлечет из него ключи
@keys = keys(%$hptr);
print $$ptr, "\n"; # Разыменовываем указатель на скаляр
</source>
В предыдущем примере с хеш-массивом мы сначала разыменовали ссылку на хеш-массив, указав символ <code>%</code> в начале, а только потом работали с массивом как обычно, передав его функции <code>keys</code>. Perl позволяет также обращаться к данным массивов непосредственно из ссылки (когда это удобно) при помощи операции разыменовывания массивов <code>-></code>.
<source lang="perl">
print $aptr->[0], $hptr->{name}, "\n";
</source>
Подробнее ссылки мы рассмотрим в отдельной главе (''см.'' [[{{Root book name}}/Ссылки|Ссылки]]).
=== Операции связывания ===
Регулярные выражения являются визитной карточкой Perl, который снабжен различными операциями для проверки по ним. Сюда входят поиск (<code>m//</code>), подстановка (<code>s///</code>) и замена (<code>tr///</code>). По умолчанию они работают с переменной <code>$_</code>.
Кроме них есть операции связывания <code>=~</code> и <code>!~</code>, которые выражают соответственно ''совпадает с шаблоном'' и ''не совпадает с шаблоном''.
Регулярные выражения являются очень обширной темой в Perl, поэтому они будут рассмотрены в отдельной главе (''см.'' [[{{Root book name}}/Работа со строками и регулярные выражения|Работа со строками и регулярные выражения]]).
==== Умное связывание ====
Начиная с 5.10.1 и до 5.37.10 в Perl поддерживалась операция умного связывания, которая умеет сравнивать переменные, хранящие разные типы данных. Данная бинарная операция обозначается двумя тильдами <code>~~</code> и изначально была введена для поддержки оператора <code>given..when</code> (в котором она используется неявно), о котором немного рассказано в следующей главе (см. [[Некоторые сведения о Perl 5/Операторы#Given..when|Операторы#Given..when]]).
Эта операция является особенной, так как её контекст определяется не на этапе компиляции, а во время исполнения программы. Для этого она определяет какой тип данных хранится в её операндах и, в зависимости от этих типов, выбирает механизм сравнивания, причем фактические типы в операндах не обязательно должны совпадать, что и делает её «умной». Если в понимании выбранного механизма операнды равны, она возвращает 1, иначе пустую строку <code>""</code>.
В Perl 5.37.10 данная операция была отмечена как неудачный эксперимент и окончательно будет вырезана в 5.42. Здесь мы о ней упоминаем, чтобы предостеречь не использовать её, хотя следует отметить сколько релизов ей удалось пережить.
=== Именованные унарные операции ===
В Perl есть большое число встроенных функций с одним аргументом, которые с точки зрения синтаксического анализатора являются ''унарными именованными операциями''<ref>Слово ''именованный'' подчеркивает тот факт, что у операции есть имя в виде обозначающего слова, при этом они не требуют скобок вокруг аргумента, копируя операции с одним символом.</ref>. Такие операции подобны функциям, но не требуют скобок вокруг параметра, как того требуют простые функции. О том, является ли функция такой операцией, можно узнать из документации. К таким операциям относятся например <code>do</code>, <code>eval</code>, <code>return</code>, <code>goto</code>, <code>sin</code>, <code>sqrt</code> и другие, а также все операции проверок файлов типа <code>-e</code> (файл существует), <code>-w</code> (в файл можно писать) и другие.
Ключевая особенность именованных унарных операций в Perl заключается в их приоритете: они имеют более высокий приоритет, чем большинство бинарных операций, но ниже, чем скобки и операция стрелки (<code>-></code>).
По ходу изложения мы затронем многие из этих операций.
=== Операции ввода/вывода ===
Это операции, которые направлены на вывод результатов в файлы или ввод данных в программу из них. Так, унарную именованную операцию <code>print</code> можно рассматривать как операцию вывода.
Чтобы обеспечить ввод данных в программу из файлов, нужно сначала их открыть и получить специальный программный объект, называемый ''дескриптором файла''. Дескриптор файла может быть получен через операцию в виде угловых скобок <code><дескриптор_файла></code>:
<source lang="perl">
open(DFILE, "net.cfg"); # Открываем файл. Идентификатор дескриптора DFILE может быть произвольным
@content = <DFILE>; # Читаем файл в массив
</source>
Если бы мы открыли файл на запись, то мы можем перенаправить запись в него так
<source lang="perl">
print MYFILE "Hello";
</source>
Тема ввода/вывода является очень обширной, поэтому она будет рассмотрена в отдельной главе (''см.'' [[{{Root book name}}/Ввод и вывод|Ввод и вывод]]).
== Выражения ==
Понимание того, как интерпретатор Perl парсит выражения, поможет вам лучше понять как они должны составляться. ''Выражение'' — это несколько операндов, объединенных одной и более операциями. Операнд в свою очередь также может быть выражением, т.е. одно общее выражение это конгломерат подвыражений.
Синтаксический анализатор Perl, во время разбора выражения, пытается выделить в нем элементарные члены — ''термы''<ref>Если делать сравнение с естественным языком, то терм является тем же самым, что и ''слово'' в предложении.</ref>, а потом соединить их знаками операций, причем он выстраивает операции в некотором порядке, учитывая приоритет.
В Perl термами являются литералы, переменные, выражения в скобках, закавыченная строка символов, а также функции с параметрами в скобках. Только терм может быть операндом. Отметим, что в Perl отсутствуют истинные функции: по существу они являются списковыми, унарными именованными операциями, ведущими себя как функции. Вызывая функцию, мы выполняем списковую или унарную операцию, которая возвращает некоторый результат.
<source lang="perl">
# Следующее выражение демонстрирует списковую операцию. В ней сначала
# вычисляется как терм каждый элемент списка, а затем каждый элемент выводится
# на печать списковой операции.
print 2 + 2 * 25 / 4, ' ', 16; # Напечатает '14.5 16'
# Следующее выражение дает аналогичный результат. Избыточные скобки
# порождают массив, передаваемый операции.
print (2 + 2 * 25 / 4, ' ', 16); # Напечатает '14.5 16'
# Теперь попробуем добавить конкатенацию:
print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 16'
# Обратите внимание, что результат конкатенации потерялся (нет символа '$'
# в выводе). Это произошло потому, что левый операнд конкатенации, как терм
# по приоритету вычисляется первым, т.е. print выводит результат своего терма
# в аргументе, а не результат конкатенации.
# Допустимо
print print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 161$'
# Появление единицы объясняется тем, что внутренний print вернул код 1, который
# был преобразован в строку и был объединен конкатенацией
# c '$'. Так как между внутренним и внешним print нет переноса
# строки, получилась такая склеенная строка.
</source>
При синтаксическом разборе выражений и операторов, как термы рассматриваются конструкции <code>do{}</code> и <code>eval{}</code>, вызовы подпрограмм и методов объектов, анонимные конструкторы массивов скаляров и хешей, а также все операции ввода/вывода.
Еще один немаловажный термин это ''контекст операции''. Операции и термы вычисляются в некотором контексте, от которого зависит поведение операции и возвращаемое ей значение. Существует два основных контекста: ''скалярный'' и ''списковый''. В скалярном контексте интерпретатор пытается вывести из терма скаляр, а в списковом контексте — список литералов <code>qw{}</code>. Контекст определяет обычно тип данных левого операнда. Так, если он является скаляром, то операция выполняется в скалярном контексте; если массив скаляров — списковом контексте. Некоторые операции способны распознавать в каком контексте они исполняются и возвращать свой результат в зависимости от него (универсальные операции).
<source lang="perl">
# В списковой операции print перед списком можно указать выходной дескриптор:
print STDOUT (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '16$'
# Потеря 14.5 Объясняется тем, что левый операнд конкатенации
# находится в скалярном контексте, а в нем операция запятая
# возвращает последний операнд как результат.
# Уберем скобки:
print STDOUT 2 + 2 * 25 / 4, ' ', 16 . '$'; # Напечатает '14.5 16$'
# Теперь каждый элемент списка после вычисления
# передается списковой операции, каждый в скалярном контексте.
</source>
Так как в Perl типизация динамическая<ref>Другими словами, все операции в Perl изначально перегружены.</ref>, понятие контекста операции является аналогом операции ''приведения типов данных'', которая есть в типизированных языках программирования. В большинстве ситуаций Perl знает лучше, в каком контексте вызывать операцию, но иногда приходится ему помогать, используя синтаксические конструкции и специальные функции. Умение предсказывать, в каком контексте выполнится операция в выражении, позволяет программисту Perl придумывать подчас очень запутанные (но с другой стороны короткие) конструкции, которые не могут себе позволить другие языки программирования из-за типизации. Мы рассмотрим некоторые случаи ниже.
<source lang="perl">
# В простом случае левый операнд задает контекст операции.
@arr = ("apple", "pear", "grapes", "banana");
# Левый операнд скаляр, а значит присваивание работает в скалярном контексте.
# В скалярном контексте массивы в правом операнде приводятся к их размеру.
$scalar = @arr;
print "$scalar\n";
# Левый операнд список скаляров, а значит присваивание работает в списковом контексте.
# В списковом контексте присваивание будет копировать элементы из массива в правом операнде
# поэлементно в список в левом операнде. При этом список слева не должен превышать размера массива.
( $element1, $element2, $element3 ) = @arr;
print $element1
. " $element2"
. " $element3\n";
# Если слева будет переменная на массив, операция также будет в списковом контексте,
# и правый массив переопределит значение переменной.
@arr1 = (1..4);
@arr1 = @arr;
print "@arr1\n";
#-------------------------------------------------------------------------------------------------------
# Операция print всегда работает в списковом контексте и все элементы списка пытается интерпретировать
# как строки.
# В стандартной библиотеке Perl есть функция localtime, которая в списковом контексте возвращает
# список из 9 элементов, кодирующих текущую дату.
print localtime; print "\n";
# Выведет примерно такое: 3219121301251120
# Однако, localtime умеет работать в скалярном контексте, в котором выводит дату в формате текущей локали,
# но этот контекст не может использоваться по умолчанию, так как компилятор только знает о том, что
# print умеет работать в списковом контексте.
#
# Этот тот случай, когда мы можем подсказать компилятору выполнить localtime в скалярном контексте,
# чтобы напечатать дату в читаемой форме. Для этого нужно воспользоваться функцией scalar, которая
# принуждает интерпретировать выражение в своем аргументе исключительно в скалярном контексте.
print scalar localtime; print "\n";
# В локали LANG_EN будет печататься в формате "Mon Jan 13 12:32:17 2025".
# Можно поступить проще и привести компилятор к скалярному контексту через другую операцию, которая
# этот контекст использует. Например, операция конкатенации строк всегда работает в скалярном контексте.
# Если компилятор увидит выражение, использующее скалярный контекст, он будет использоваться для всех
# операндов операции. Таким образом, такого же результата мы добиваемся, если напишем так:
print localtime . "\n";
# "Mon Jan 13 12:32:17 2025"
#-------------------------------------------------------------------------------------------------------
# Рассмотрим более сложный пример.
# Пусть дана такая строка:
$word = "111example222";
# Вопрос: как посчитать в ней количество символов цифр и букв и совместить это с печатью в одном компактном выражении?
# Для ответа на вопрос "сколько символов данного класса?" здесь следует использовать регулярные выражения.
# Так как о регулярных выражениях мы будем говорить много позже, возьмите на веру, что регулярное выражение для
# поиска цифр выглядит так '/\d/g', а букв так '/[^\d\s]/g'.
#
# Примечание: '/[^\d\s]/g' не учитывает Unicode символы.
#
# Проблема кроется в том, что регулярные выражения умеют работать и в скалярном, и в списковом контекстах.
# Конкретно нам нужен списковый контекст, в котором операция возвращает массив совпадений по шаблону, который
# мы могли бы преобразовать в размер, тем самым ответив на вопрос "сколько?". Но как нам сказать, чтобы операция
# выполнялась в списковом контексте, а не скалярном, ведь специальной функции типа scalar для этого нет?
#
# Единственный способ сделать это, привести компилятор к этому контексту через другую операцию.
# Существует два распространенных приёма провоцировать списковый контекст:
# * Можно использовать операцию присваивания, в которой левый операнд это пустой массив.
# * Можно использовать операцию разыменовывания ссылки, которая будет работать в контексте типа
# разыменовывания.
# Ниже мы в одном выражении используем оба приема для демонстрационных целей
print "Number of letters: "
. ( () = $word =~ /[a-zA-Z]/g ) . "\n"
. "Number of digits: "
. ( @{ [ $word =~ /\d/g ] } ) . "\n"
;
# Number of letters: 7
# Number of digits: 6
</source>
Не расстраивайтесь, если вы ничего из предыдущих примеров не поняли, так как мы сильно забегаем в них вперед. Эти примеры призваны показать, как сильно выражения завязаны на контекст операции.
Скалярный контекст также можно разделить по типу хранимых данных на ''числовой'', ''строковый'' и ''безразличный''. Perl неспособен заранее распознать этот контекст, поэтому он просто подключает механизм неявного приведения данных, когда скаляры в операндах хранят разные типы.
Еще один специальный скалярный контекст — ''булевый''. Так как в Perl нет булева <code>true</code> или <code>false</code>, Perl анализирует строки и числа в булевом контексте, как это было описано выше: <code>0</code>, <code>"0"</code>, <code>"00"</code>, <code>"0.0"</code> и <code>""</code> это условно ЛОЖЬ, а все остальное ИСТИНА.
Еще одним контекстом является void-контекст, который используется, когда в выражении нет побочных эффектов. Так, вы можете спокойно писать в Perl такие бессмысленные конструкции (они выполняются в void-контексте):
<source lang="perl">
(1,2,3);
$n;
"Hello";
</source>
Этот контекст можно обнаружить, если интерпретатор Perl запущен в режиме вывода предупреждающих сообщений.
Еще один контекст это ''подстановочный контекст'', в котором вычисляются операции заключения в кавычки (кроме одинарных кавычек). В этом контексте подставляются переменные и интерпретируются управляющие последовательности.
Ниже для справки представлены приоритеты операций.
{|class="wikitable" style="width:100%;"
|-
! Приоритет
! Операция
! Сочетаемость
|-
|style="width:10%;"|1
|Вычисление термов и левосторонних списковых операций
|style="width:20%;"|Слева направо
|-
|2
|<code><nowiki>-></nowiki></code>
|Слева направо
|-
|3
|<code><nowiki>++</nowiki></code> <code><nowiki>--</nowiki></code>
|Не сочетаются
|-
|4
|<code><nowiki>**</nowiki></code>
|Справа налево
|-
|5
|<code><nowiki>!</nowiki></code> <code><nowiki>~</nowiki></code> <code><nowiki>\</nowiki></code> унарный <code><nowiki>+</nowiki></code> унарный <code><nowiki>-</nowiki></code>
|Справа налево
|-
|6
|<code><nowiki>=~</nowiki></code> <code><nowiki>!=</nowiki></code>
|Слева направо
|-
|7
|<code><nowiki>*</nowiki></code> <code><nowiki>/</nowiki></code> <code><nowiki>%</nowiki></code> <code><nowiki>x</nowiki></code>
|Слева направо
|-
|8
|<code><nowiki>+</nowiki></code> <code><nowiki>-</nowiki></code> <code><nowiki>.</nowiki></code>
|Слева направо
|-
|9
|<code><nowiki><<</nowiki></code> <code><nowiki>>></nowiki></code>
|Слева направо
|-
|10
|Именованные унарные операции
|Не сочетаются
|-
|11
|<code><nowiki><</nowiki></code> <code><nowiki>></nowiki></code> <code><nowiki><=</nowiki></code> <code><nowiki>>=</nowiki></code> <code><nowiki>lt</nowiki></code> <code><nowiki>gt</nowiki></code> <code><nowiki>le</nowiki></code> <code><nowiki>ge</nowiki></code>
|Не сочетаются
|-
|12
|<code><nowiki>==</nowiki></code> <code><nowiki>!=</nowiki></code> <code><nowiki><=></nowiki></code> <code><nowiki>eq</nowiki></code> <code><nowiki>ne</nowiki></code> <code><nowiki>cmp</nowiki></code>
|Не сочетаются
|-
|13
|<code><nowiki>&</nowiki></code>
|Слева направо
|-
|14
|<code><nowiki>|</nowiki></code> <code><nowiki>^</nowiki></code>
|Слева направо
|-
|15
|<code><nowiki>&&</nowiki></code>
|Слева направо
|-
|16
|<code><nowiki>||</nowiki></code>
|Слева направо
|-
|17
|<code><nowiki>..</nowiki></code> <code><nowiki>...</nowiki></code>
|Не сочетаются
|-
|18
|<code><nowiki>?:</nowiki></code>
|Справа налево
|-
|19
|<code><nowiki>=</nowiki></code> <code><nowiki>**=</nowiki></code> <code><nowiki>+=</nowiki></code> <code><nowiki>-=</nowiki></code> <code><nowiki>.=</nowiki></code> <code><nowiki>*=</nowiki></code> <code><nowiki>/=</nowiki></code> <code><nowiki>%=</nowiki></code> <code><nowiki>x=</nowiki></code> <code><nowiki>&=</nowiki></code> <code><nowiki>|=</nowiki></code> <code><nowiki>^=</nowiki></code> <code><nowiki><<=</nowiki></code> <code><nowiki>>>=</nowiki></code> <code><nowiki>&&=</nowiki></code> <code><nowiki>||=</nowiki></code>
|Справа налево
|-
|20
|<code><nowiki>,</nowiki></code> <code><nowiki>=></nowiki></code>
|Слева направо
|-
|21
|Правосторонние списковые операции
|Не сочетаются
|-
|22
|<code><nowiki>not</nowiki></code>
|Справа налево
|-
|23
|<code><nowiki>and</nowiki></code>
|Слева направо
|-
|24
|<code><nowiki>or</nowiki></code> <code><nowiki>xor</nowiki></code>
|Слева направо
|}
* В данной таблице чем выше приоритет операции и чем левее она стоит (в большинстве случаев), тем раньше она выполняется.
* Приоритет списковой операции зависит от ее места в выражении и может изменяться от наивысшего к наименьшему. Все операции в выражении, стоящие слева от списковой, имеют наивысший приоритет по отношению к ней (смотрите 21 приоритет).
* Свойство сочетаемости означает, что операции одного приоритета могут быть в одном выражении, причем порядок их разрешения может быть слева направо или справа налево.
* Скобки позволяют изменить порядок вычисления, определяемому в обычных условиях приоритетом и сочетаемостью. Скобки всегда наделяют выражение в них наивысшим приоритетом.
== Примечания ==
{{примечания}}
3j7qw6mn9xq4auk8r4v26prduwt8h9l
269940
269931
2026-09-03T08:24:16Z
ScriptMaster
15708
/* Побитовые операции */
269940
wikitext
text/x-wiki
{{Навигация учебника}}
''Операция'' — это некоторые действия над операндами, результатом которых является новое значение. В Perl есть десятки операций, большинство из которых мы рассмотрим в этой главе, а другие в последующих. Операции можно делить по типу выполняемых ими действий (например, арифметические, логические и т.д.), по объекту, на который они воздействуют (строковые, списковые, файловые), а иной раз сложно отнести их к какой-то категории.
Большинство операций работает с двумя операндами, которые записываются по обе стороны от знака операции (''бинарная операция''). Например в операции сложения операндами выступают слагаемые. Для других операций может требоваться один операнд (''унарная операция'') или три (''тернарная операция''). В некоторых операциях какие-то операнды можно опускать.
Серия операций, записанная единым куском образует ''выражение''. Любое выражение интерпретируется слева направо, при этом порядок исполнения операций внутри выражения зависит от приоритета операций. Приоритеты операций в выражении можно менять через круглые скобки, которые дают более высокий приоритет.
== Арифметические операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Арифметические операции'''</center>
|-
|<center><code>+</code></center>
|style="width:10%;"|<center><code>6.35+8.31</code></center>
|style="width:30%;"|'''Сложение'''. Выполняет арифметическое сложение
|<center><code>-</code></center>
|style="width:10%;"|<center><code>9.15-5</code></center>
|style="width:30%;"|'''Вычитание'''. Выполняет арифметическое вычитание
|-
|<center><code>*</code></center>
|<center><code>2*2</code></center>
|'''Умножение'''
|<center><code>/</code></center>
|<center><code>15/3</code></center>
|'''Деление'''
|-
|<center><code>**</code></center>
|<center><code>4**2</code></center>
|'''Возведение в степень'''
|<center><code>%</code></center>
|<center><code>10%5</code></center>
|'''Остаток от деления'''
|-
| colspan="6" | <center>'''Унарные арифметические операции'''</center>
|-
|<center><code>+</code></center>
|<center><code>+5</code></center>
|'''Унарный плюс'''
|<center><code>-</code></center>
|<center><code>-6</code></center>
|'''Унарный минус'''. Делает положительное число отрицательным, а отрицательное — положительным
|-
| colspan="6" | <center>'''Инкремент/декремент'''</center>
|-
|<center><code>++</code></center>
|<center><code>$i = 5, $j=$i++;</code></center>
|'''Постфиксный инкремент'''. Увеличивает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 6, потому что инкремент выполняется после присваивания
|<center><code>++</code></center>
|<center><code>$i = 5, $j=++$i;</code></center>
|'''Префиксный инкремент'''. В отличие от постфиксного инкремента, значение в операнде будет увеличено до всех прочих операций над ним. В данном примере в обеих переменных будет 6
|-
|<center><code>--</code></center>
|<center><code>$i = 5, $j=$i--;</code></center>
|'''Постфиксный декремент'''. Уменьшает значение переменной на единицу после всех прочих операций над переменной. В примере значение в <code>$j</code> будет 5, а в <code>$i</code> значение будет 4, потому что декремент выполняется после присваивания
|<center><code>--</code></center>
|<center><code>$i = 5, $j=--$i;</code></center>
|'''Префиксный декремент'''. В отличие от постфиксного декремента, значение в операнде будет уменьшено до всех прочих операций над ним. В данном примере в обеих переменных будет 4
|}
* В основном арифметические операции направлены на работу с числами, однако операндами могут выступать и строки, причем не обязательно, чтобы они конвертировались в числа. В таких случаях интерпретатор сначала попытается выделить число из символов, начиная с первого. Если выделить число не получается, то результатом конвертации будет 0. Так, строка <code>5gh4</code> будет конвертирована в 5, а <code>g130</code> — в 0.
* Если в операции остаток от деления (<code>%</code>) второй операнд является вещественным числом и имеет дробную часть, то он предварительно преобразуется в целое число путем простого отброса дробной части.
* Отрицательное число <u>нельзя</u> возводить не в целую степень. Интерпретатор не будет выдавать никакой ошибки, но при этом результат всегда будет 0.
* Бинарные арифметические операции выполняются в ''скалярном контексте''. Это означает, что интерпретатор будет ожидать в операндах скалярные переменные. Если в операндах будут массивы, то они будут преобразованы к числу элементов в массиве, а в случае хеш-массива — к числу использованных хеш-записей в нем.
* Инкремент и декремент можно применять к строковым данным. В случае инкремента, если строка состоит только из латинских букв, то результатом будет новая строка, в которой последняя буква будет заменена на следующую по порядку в алфавите, причем строчная буква заменяется строчной, а прописная — прописной. Аналогично происходит, если строка завершается цифрой. Для декремента Perl будет пытаться конвертировать строку в число по обычным правилам, описанным выше.
== Строковые операции ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Строковые операции'''</center>
|-
|<center><code>.</code></center>
|style="width:10%;"|<center><code>"a"."b"</code></center>
|style="width:30%;"|'''Конкатенация строк'''. Объединяет две строки в одну
|<center><code>x</code></center>
|style="width:10%;"|<center><code>"a" x 3</code></center>
|style="width:30%;"|'''Повторитель'''. Повторяет строку в левом операнде N число раз и возвращает из этого строку
|}
* Строковые операции обычно направлены на строковый тип данных, однако можно использовать и числа. В этом случае числа будут преобразованы в строки.
* Оператор конкатенации не вставляет пробелы между строками. Обычно для объединения с пробелами прибегают к функции <code>join</code>.
* Для операции ''повторения'' левый операнд всегда приводится к строковому значению. Правым операндом должно быть целое число, либо строка, которая может быть приведена к целому числу. В случае вещественного числа, дробная часть будет просто отбрасываться.
* Для операции ''повторения'' левым операндом может быть список слов, заключенный в круглые скобки, т.е. эта операция может работать в ''списковом контексте''. В таком случае список будет повторен указанное число раз. Такой прием используется для порождения списка, передаваемого конструктору массива.
* Знак операции повторения следует всегда отделять хотя бы одним пробелом от его операндов, так как в некоторых ситуациях интерпретатор может принять его за часть имени переменной.
* С операцией повторения связана устойчивая идиома, используемая для быстрого заполнения одинаковыми значениями:<source lang="perl">
@arr[1..5] = (1) x 5;
print "@arr\n";
# 1 1 1 1 1
@hash{qw{ apples bananas pears }} = (0) x 3;
print "@{[%hash]}\n";
# apples 0 bananas 0 pears 0
</source>Если в левом операнде операции повторения используется массив, то операция будет выполняться в списковом контексте, в котором она повторит элементы указанное число раз в новом массиве. Если этот массив присвоить срезу массива или хэша (слева от присваивания), то сгенерированные повторением элементы будут присвоены элементам среза. На практике часто используется прием получения хеша из параметров командной строки, переданных сценарию:<source lang="perl">
my %exists;
@exists{@ARGV} = (1) x @ARGV;
# \ \____ В скалярном контексте развернется в число элементов массива @ARGV.
# \___________________ Получается хеш, в котором ключами будут элементы командной строки.
</source>
== Логические операции ==
=== Операции отношения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Числовые отношения'''</center>
|-
|<center><code>></code></center>
|style="width:10%;"|<center><code>2>1</code></center>
|style="width:30%;"|'''Больше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>>=</code></center>
|style="width:10%;"|<center><code>3>=1</code></center>
|style="width:30%;"|'''Больше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><</code></center>
|<center><code>-3<0</code></center>
|'''Меньше'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code><=</code></center>
|<center><code>0<=3</code></center>
|'''Меньше либо равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>==</code></center>
|<center><code>3==3</code></center>
|'''Равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>!=</code></center>
|<center><code>1!=2</code></center>
|'''Не равно'''. Возвращает 1, если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code><=></code></center>
|<center><code>15<=>63</code></center>
|'''Один из операндов больше другого'''. Возвращает -1, если правый операнд больше левого; 1 — правый операнд меньше левого; 0 — операнды равны
|
|
|
|-
| colspan="6" | <center>'''Строковые отношения'''</center>
|-
|<center><code>eq</code></center>
|<center><code>"a" eq "a"</code></center>
|'''Строки лексикографически равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ne</code></center>
|<center><code>"a" ne "b"</code></center>
|'''Строки лексикографически не равны'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>lt</code></center>
|<center><code>"a" lt "c"</code></center>
|'''Левая строка лексикографически меньше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>le</code></center>
|<center><code>"a" le "c"</code></center>
|'''Левая строка лексикографически меньше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>gt</code></center>
|<center><code>"d" gt "e"</code></center>
|'''Левая строка лексикографически больше правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|<center><code>ge</code></center>
|<center><code>"d" ge "e"</code></center>
|'''Левая строка лексикографически больше или равна правой'''. Возвращает "1", если ИСТИНА и <code>""</code>, если ЛОЖЬ
|-
|<center><code>cmp</code></center>
|<center><code>"a" cmp "b"</code></center>
|'''Лексикографически один из операндов не равен другому'''. Возвращает -1, если правый операнд лексикографически больше левого; 1 — правый операнд лексикографически меньше левого; 0 — операнды лексикографически равны
|
|
|
|}
* Обратите внимание, что операции отношения скопированы с командной оболочки Bourne Shell, но там все было наоборот: <code>eq</code>, <code>ne</code>, <code>lt</code>, <code>le</code>, <code>gt</code> и <code>ge</code> применялись к числам, а привычные нам знаки использовались для лексикографического сравнивания. Об этом нужно помнить, когда вы переключаетесь с программирования для командной оболочки на программирование для Perl.
* Лексикографическое сравнивание означает, что сравниваются ASCII кодировки символов. Так символ <code>a</code> лексикографически будет меньше символа <code>b</code>, потому что код последнего (98) больше первого (97). При лексикографическом сравнивании строк учитывается код каждого символа в строке, а также длина строк.
* ИСТИНА для числовых отношений это число 1, а для строковых — строка <code>"1"</code>.
* В общем случае следует быть осторожным при сравнивании вещественных чисел с целыми, так как тут вступает фактор округления чисел. Следует отметить, что вещественные числа вообще нельзя сравнивать на равенство, так как результат практически всегда будет неправильным. В этом случае следует сравнивать разницу двух вещественных чисел с очень малым числом (погрешностью). Можно использовать и другой приём, когда числа преобразуются в строки и сравниваются как строки.
=== Булевы выражения ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Булевы операции'''</center>
|-
|<center><code>&&</code></center>
|style="width:10%;"|<center><code>1 && 1</code></center>
|style="width:30%;"|'''Конъюнкция'''
|<center><code><nowiki>||</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>0 || 1</nowiki></code></center>
|style="width:30%;"|'''Дизъюнкция'''
|-
|<center><code>!</code></center>
|<center><code>! 0</code></center>
|'''Логическое отрицание'''
|<center><code>xor</code></center>
|<center><code>1 xor 1</code></center>
|'''Исключающее ИЛИ'''
|-
|<center><code>and</code></center>
|<center><code>1 and 1</code></center>
|'''Конъюнкция'''. ''Синоним''
|<center><code>or</code></center>
|<center><code>0 or 1</code></center>
|'''Дизъюнкция'''. ''Синоним''
|-
|<center><code>not</code></center>
|<center><code>not 1</code></center>
|'''Логическое отрицание'''. ''Синоним''
|<center><code>^^</code></center>
|<center><code>1 ^^ 1</code></center>
|'''Исключающее ИЛИ'''<ref>Знак операции появился с версии интерпретатора 5.40.</ref>
|-
|<center><code>//</code></center>
|<center><code>$a // $b</code></center>
|'''Слияние'''
|
|
|
|}
* Операции <code>or</code>, <code>and</code>, <code>not</code> и <code>xor</code> появились в Perl, начиная с версии 5.001. Отличие их от аналогичных операций в виде символов в том, что они имеют меньший приоритет при вычислении в сложных выражениях. Такой пониженный приоритет иногда требуется в выражениях по типу <code>$num != 0 or print "\$num == 0";</code>
* Для булевых операций ИСТИНОЙ признается любое скалярное значение не равное <code>0</code>, <code>"0"</code> и <code>""</code>, в противном случае это ЛОЖЬ. Значение <code>undef</code> также приравнивается ко ЛЖИ.
* Булевы операции конъюнкции и дизъюнкции вычисляются по укороченной схеме. Это означает, например, что если при конъюнкции ЛОЖЬ в некотором операнде встречается раньше, чем закончилось выражение, вычисление прекращается на этом операнде с общим результатом ЛОЖЬ.
* Хотя булевы операции рассчитаны в основном на то, чтобы использовать их с числами, некоторые из них можно использовать для работы со строками. Полезной здесь оказывается дизъюнкция, которая может быть использована для записи укороченного присваивания значений по умолчанию, что показано в следующем примере: <source lang="perl">
# Эта процедура просто возвращает строку, переданную в аргументе, но если он не передается,
# то она назначит ему значение по умолчанию.
sub procedure {
my ($arg) = @_;
$arg ||= "default_value";
return $arg;
}
# Вызов без аргумента
print "Result: " . procedure . "\n";
# Result: default_value
# Вызов с аргументом
print "Result: " . procedure "some_value" . "\n";
# Result: some_value
</source> Также, в случаях, когда неопределенные значения недопустимы, она может использоваться для выбора из альтернативы: <source lang="perl">
$var = undef;
$var = $var || "default_value";
print "$var\n";
# default_value
# Размер цепочки может быть сколько угодно большим. В данном примере мы использовали
# undef для демонстрации, но представьте на их местах переменные. Значение по умолчанию
# выбирается с разным приоритетом (первое не пустое с левого края).
$value = undef || undef || undef || "default_value";
print "$value\n";
</source>
* Операция слияния (<code>//</code>) работает так: если левый операнд определен (''defined''), то вычисляется выражение, записанное в первом операнде и его результат возвращается как результат всей операции, иначе, если левый операнд не определен, но определен второй операнд, то его результат возвращается как результат всей операции. Если не определены оба операнда, то возвращается <code>undef</code> как результат всей операции. Технически операция похожа на тернарную операцию (см. ниже), но нацелена она на проверку условия ''определен'' / ''не определен''. Операция имеет пониженный приоритет, поэтому в сложных выражениях её обычно следует оставлять в скобках. Вот примеры, когда она может использоваться:<source lang="perl">
# Взять значение в кеше или вызвать процедуру по извлечению значения из хранилища.
$data = $cache{ $key } // get_from_storage($key);
# ----------------------------------------------------------
$x = 10;
$y = undef;
$z = 5;
# Это выражение ...
$result = $x + ($y // $z);
# ... является коротким способом записать такое
unless (defined $y) {
$result = $x + $z;
} else {
$result = $x + $y;
}
# ----------------------------------------------------------
$discounted_price = 60;
$base_price = 100;
$tax_rate = undef;
$price = $discounted_price // $base_price;
# Если налоговая ставка не определена, то она равняется нулю.
$final = $price * (1 - ($tax_rate // 0));
# ----------------------------------------------------------
$default_config = 'default';
# Будет присвоено первое определенное значение.
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# ----------------------------------------------------------
$default_config = undef;
$config_value = $ARGV[0] // $ENV{'PARAM'} // $default_config;
# Процедура compute_expensive_value будет вызвана только если config_value
# не определена.
$config_value //= compute_expensive_value();
sub compute_expensive_value {
'computed'
}
print "$config_value\n";
</source>
=== Побитовые операции ===
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Побитовые операции'''</center>
|-
|<center><code><nowiki>&</nowiki></code></center>
|style="width:10%;"|<center><code>3 & 2</code></center>
|style="width:30%;"|'''Побитовое И'''. Выполняет конъюнкцию двух чисел побитово
|<center><code><nowiki>|</nowiki></code></center>
|style="width:10%;"|<center><code><nowiki>3 | 2</nowiki></code></center>
|style="width:30%;"|'''Побитовое ИЛИ'''. Выполняет дизъюнкцию двух чисел побитово
|-
|<center><code>^</code></center>
|<center><code>3 ^ 3</code></center>
|'''Побитовое исключающее ИЛИ'''
|<center><code>~</code></center>
|<center><code>~0</code></center>
|'''Побитовое отрицание'''
|-
|<center><code><<</code></center>
|<center><code>1 << 1</code></center>
|'''Побитовый сдвиг влево'''
|<center><code>>></code></center>
|<center><code>2 >> 1</code></center>
|'''Побитовый сдвиг вправо'''
|}
* Побитовые операции работают только с числами, если хотя бы один операнд является числовым типом.
* До версии 5.20 операции пытались автоматически работать со стоковыми операндами. Если '''хотя бы один''' операнд являлся строкой (которая не может быть сконвертирована в число, потому что записана не только цифрами), то побитово выполняются операции с кодом каждого символа, даже если другой операнд записан только цифрами. Если при этом строки разной длины, то меньшая из них дополняется символами <code>\000</code> до равенства. Но, как позже выяснилось, такое автоматическое поведение приводило к трудноуловимым ошибкам.
* Начиная с Perl 5.20, побитовые операции для числовых и строковых типов данных были строго отделены директивой <code>use feature 'bitwise'</code>, а с версии 5.28 она всегда включается автоматически. Операции <code>&</code>, <code>|</code>, <code>^</code>, <code>~</code> работают только с числовым типом данных, а <code>&.</code>, <code>|.</code>, <code>^.</code>, <code>~.</code> работают со строковым типом данных:<source lang="perl">
use feature "bitwise";
$a = "10";
$b = "12";
printf "string 'a': %vb\n", $a;
printf "number 'a': %06b\n", ($a + 0);
printf "string 'b': %vb\n", $b;
printf "number 'b': %06b\n", ($b + 0);
printf "a & b: %vb\n", ($a & $b);
printf "a &. b: %vb\n", ($a &. $b);
printf "a & b: %vb (numeric)\n", ($a + 0 & $b + 0);
</source><source lang="bash">
string 'a': 110001.110000
number 'a': 001010
string 'b': 110001.110010
number 'b': 001100
a & b: 111000
a &. b: 110001.110000
a & b: 111000 (numeric)
# Пояснения:
# До версии 5.20 интерпретатор будет завершаться с ошибкой, так как операций с точкой
# ещё не существовало. Директива выше гарантирует, что bitwise поддерживается.
# Так как оба операнда могут быть корректно сконвертированы в числа, то мы можем увидеть,
# что операция & работает с операндами как с числами (001010 & 001100 = 001000 или 8, что в
# в ASCII соответствует коду 56, который в двоичной системе 111000), а операция &. работает с
# каждым символом в строке по отдельности, поэтому получается результат 110001.110000.
</source>
* Все вещественные числа преобразуются к целым простым отбрасыванием дробной части:<source lang="perl">
# Выражение ...
$result = 3.5 & 5.9;
# ... и ...
$result = 3 & 5;
# ... работают одинаково.
</source>
* Допустимо использовать любой формат представления чисел: десятичный, шестнадцатеричный и восьмеричный.
== Операции присваивания ==
{|class="wikitable" style="width:100%;"
|-
! Операция
! Пример
! Описание
! Операция
! Пример
! Описание
|-
| colspan="6" | <center>'''Операции присваивания'''</center>
|-
|<center><code>=</code></center>
|style="width:10%;"|<center><code>$i = 2</code></center>
|style="width:30%;"|'''Простое присваивание'''
|<center><code>**=</code></center>
|style="width:10%;"|<center><code>$i **= 2</code></center>
|style="width:30%;"|'''Возведение в степень с присваиванием'''
|-
|<center><code>+=</code></center>
|<center><code>$i += 2</code></center>
|'''Сложение с присваиванием'''
|<center><code>-=</code></center>
|<center><code>$i -= 2</code></center>
|'''Вычитание с присваиванием'''
|-
|<center><code>.=</code></center>
|<center><code>$i .= 2</code></center>
|'''Конкатенация с присваиванием'''
|<center><code>*=</code></center>
|<center><code>$i *= 2</code></center>
|'''Умножение с присваиванием'''
|-
|<center><code>/=</code></center>
|<center><code>$i /= 2</code></center>
|'''Деление с присваиванием'''
|<center><code>%=</code></center>
|<center><code>$i %= 2</code></center>
|'''Остаток от деления с присваиванием'''
|-
|<center><code>x=</code></center>
|<center><code>$i x= 2</code></center>
|'''Повторение с присваиванием'''
|<center><code>&=</code></center>
|<center><code>$i &= 2</code></center>
|'''Поразрядное И с присваиванием'''
|-
|<center><code><nowiki>|=</nowiki></code></center>
|<center><code><nowiki>$i |= 2</nowiki></code></center>
|'''Поразрядное ИЛИ с присваиванием'''
|<center><code>^=</code></center>
|<center><code>$i ^= 2</code></center>
|'''Поразрядное исключающее ИЛИ с присваиванием'''
|-
|<center><code><<=</code></center>
|<center><code>$i <<= 2</code></center>
|'''Поразрядный сдвиг влево с присваиванием'''
|<center><code>>>=</code></center>
|<center><code>$i >>= 2</code></center>
|'''Поразрядный сдвиг вправо с присваиванием'''
|-
|<center><code>&&=</code></center>
|<center><code>$i &&= 2</code></center>
|'''Конъюнкция с присваиванием'''
|<center><code><nowiki>||=</nowiki></code></center>
|<center><code><nowiki>$i ||= 2</nowiki></code></center>
|'''Дизъюнкция с присваиванием'''
|-
|<center><code>^^=</code></center>
|<center><code>$i ^^= 1</code></center>
|'''Исключающее ИЛИ с присваиванием'''<ref>Поддерживается с версии 5.42.</ref>
|<center><code>//=</code></center>
|<center><code>$value //= $default</code></center>
|'''Слияние с присваиванием'''
|}
* Простое присваивание является бинарной операцией, правый операнд которой является выражением, а левый — определяет участок памяти (в форме переменной), в который запишется результат выражения. Операция присваивания возвращает левый операнд как результат выражения. По этой причине следующая запись будет синтаксически правильной<source lang="perl">
($i = $j) = 5;
# Если развернуть эту конструкцию, то получается
$i = $j;
$i = 5; # Так как выражение в скобках вернет $i
</source>
* Как в языке Си, в Perl есть составное присваивание, совмещающее в себе операцию и присваивание. Операции с присваиванием также возвращают левый операнд, как результат выражения<source lang="perl">
$b = 1;
$a = ($b += 3); # Аналогично $a = $b = 4
$a += ($b += 3); # Аналогично $a = $a + $b + 3
(($a += 2) **= 2 ) -= 1; # Аналогично $a = ($a + 2) ** 2 - 1
</source>Обратим внимание, что в большинстве ситуаций в сложных выражениях ставить скобки для операций присваивания обязательно, потому что, как вы узнаете ниже, они парсятся интерпретатором справа налево.
== Списковые операции ==
''Списковая операция'' — это операция над списком значений. Операции, которым передаются списки и кажущиеся функциями, на самом деле являются списковыми операциями, например <code>print</code>. Это связано с тем, что списковые операции разрешают помещать свои аргументы в скобки. Так, следующие вызовы равнозначны
<source lang="perl">
print $a, "hello", $b;
print ($a, "hello", $b);
</source>
Списковые операции ожидают на входе список скаляров в некоторой форме, а на выходе отдают тот же список, но модифицированный некоторым образом, либо новый список. Так, следующие операции являются списковыми:
* Создание и конкатенация массивов<source lang="perl">
@array1 = (1,2,3);
@array2 = (4,5,6);
@array3 = (@array1, @array2);
</source>
* Встроенные операции, связанные с манипуляцией значением(ми) в массиве<source lang="perl">
@array = (1,2,3);
push @array, 4; # (1, 2, 3, 4)
pop @array; # (1, 2, 3); 4 возвращается как результат
shift @array; # (2, 3); 1 возвращается как результат
unshift @array, 5; # (5, 2, 3)
splice @array, 0, 2; # (3); (5, 2) возвращается как результат
</source>
* Трансформация (новый массив возвращается как результат)<source lang="perl">
@array = qw { a b c d e f };
# Применение предиката к каждому элементу в списке
print "$_\n" foreach map { $_ . $counter++ } @array;
#
# Вызов map в модификаторе foreach возвращает новый массив, каждый элемент которого
# печатается print слева от модификатора. В этом примере процедура предиката к каждому
# элементу исходного массива добавляет порядковый номер, в результате чего получается
#
# a0
# b1
# c2
# d3
# e4
# f5
#
# Сформировать новый массив, элементы которого хранятся в обратном порядке
print "$_\n" foreach reverse @array;
# f
# e
# d
# c
# b
# a
# Сортировка
use List::Util; # Подключаем модуль для перемешивания элементов для дальнейшего примера.
@array = List::Util::shuffle(@array); # Перемешиваем элементы в случайном порядке.
print "@{array}\n"; # Вывод будет отличаться при каждом вызове: "b f d a e c"
@array = sort @array; # Снова в алфавитном порядке.
print "@{array}\n"; # "a b c d e f"
</source>
* Фильтрующие операции (возвращают подмножество исходного массива как результат)<source lang="perl">
@array = qw { a 1 5 d 6 f 8 9 h j k l 25 };
@numbers = grep /\d+/, @array; # Достаем из исходного массива только цифры
print "@{numbers}\n"; # "1 5 6 8 9 25"
@letters = grep /[a-z]+/, @array; # Достаем из исходного массива только буквы
print "@{letters}\n"; # "a d f h j k l"
</source>
* Агрегирующие операции (как правило, возвращают все элементы массивов одним значением)<source lang="perl">
@words = qw { apple grapes banana };
$value = join ", ", @words; # объединяет все элементы и вставляет между ними разделитель.
print "${value}\n"; # "apple, grapes, banana"
</source>
* Разворачивающие операции (наоборот, из одного значения получают список)<source lang="perl">
@numbers = 1..5;
print "@{numbers}\n"; # "1 2 3 4 5"
$fruits = "apple, banana, grapes";
@fruits = split ", ", $fruits;
print "@{fruits}\n"; # "apple banana grapes"
</source>
Подробнее о пользовательских списковых операциях будет в главе, посвященной функциям.
=== Списковые операции all и any ===
Начиная с версии 5.42, в ядро интерпретатора были интегрированы две новые операции, помогающие работать с множествами без использования модулей. До этого эти операции приходилось подключать в виде функций из модуля <code>List::Util</code>. Вероятно, добавление этих операций в ядро было вдохновлено языком Python, в котором они существуют в виде операторов.
<source lang="perl">
any { <блок-предиката> } @list;
all { <блок-предиката> } @list;
</source>
Операции <code>all</code> и <code>any</code> мимикрируют под операцию <code>grep</code> и используются, чтобы вернуть логический ответ по предикату. Чтобы операция <code>all</code> вернула ИСТИНУ, необходимо чтобы её предикат вернул ИСТИНУ для каждого элемента списка. Соответственно, для операции <code>any</code> ИСТИНОЙ считается ситуация, когда предикат возвращает ИСТИНУ по меньшей мере для одного элемента списка.
Появление этих операций, хоть и с опозданием, очень кстати, так как различного рода проверки множеств — частая задача на практике. Вот лишь несколько примеров.
<source lang="perl">
use v5.42; # Директива требует интерпретатор не ниже указанной версии.
# Следующие директивы нужны по крайней мере для версии 5.42, так как на тот момент
# эти возможности были еще экспериментальными. В будущих версиях эти
# директивы не понадобятся.
use feature 'keyword_any', 'keyword_all'; # Включает операции
no warnings 'experimental::keyword_any', 'experimental::keyword_all'; # Отключает предупреждения
# Использование my в этом месте обусловлено тем, что директива use v5.42
# автоматически включает директиву strict (будет рассмотрено позже).
my @numbers = (2, 4, 6, 8, 10);
# Через специальную переменную $_ предикат получает очередной элемент списка.
if (all { $_ % 2 == 0 } @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Сравните этот код с программой, которую мы бы написали для версии интерпретатора меньше 5.42:
<source lang="perl">
use List::Util qw{ all any }; # Импортируем функции из модуля.
@numbers = (2, 4, 6, 8, 10);
# ____ Запятую можно опустить, так как у функции есть прототип. Для any она опущена.
# V
if (all { $_ % 2 == 0 }, @numbers) {
print "All numbers are even\n";
}
if (any { $_ > 9 } @numbers) {
print "At least one number is greater than 9\n";
}
</source>
Обратите внимание на то, что в старых версиях интерпретатора функции <code>all</code> и <code>any</code> всегда нужно импортировать из модуля <code>List::Util</code>. Однако не спешите полностью списывать этот модуль, так как в нём есть множество других полезных функций.
== Операции заключения в кавычки ==
Кавычки (одинарные, двойные и обратные) используются в Perl, чтобы задавать строковые литералы. Кроме этого есть еще операции, которые внешне похожи на операции заключения в кавычки, и перечислены в таблице ниже.
{|class="wikitable" style="width:100%;"
|-
! Операция
! Эквивалент
! Описание
! Операция
! Эквивалент
! Описание
|-
| colspan="6" | <center>'''Операции заключения в кавычки'''</center>
|-
|<center><code>q{}</code></center>
|style="width:10%;"|<center><code><nowiki>''</nowiki></code></center>
|style="width:30%;"|'''Строковый литерал'''. Подстановки невозможны
|<center><code>qq{}</code></center>
|style="width:10%;"|<center><code>""</code></center>
|style="width:30%;"|'''Строковый литерал'''
|-
|<center><code>qx{}</code></center>
|<center><code><nowiki>``</nowiki></code></center>
|'''Команда системы'''
|<center><code>qw{}</code></center>
|<center><code><nowiki>()</nowiki></code></center>
|'''Список слов'''. Подстановки невозможны
|-
|<center><code>m{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Поиск по регулярному выражению'''
|<center><code>qr{}</code></center>
|<center><code><nowiki>//</nowiki></code></center>
|'''Шаблон регулярного выражения'''
|-
|<center><code>s{}{}</code></center>
|<center><code>s///</code></center>
|'''Подстановка'''
|<center><code>tr{}{}</code></center>
|<center><code><nowiki>y///</nowiki></code></center>
|'''Транслитерация'''. Подстановки невозможны
|}
* Фигурные скобки в данных операциях представляют собой разделитель. Они могут быть в любой момент заменены на другой символ или любую другую пару скобок, например<source lang="perl">
q{hello}
q/hello/
q!hello!
q<\<hello\>> # Если символы разделителя есть в значении, то они должны экранироваться.
</source>Внутри разделителей разрешается любое число пробелов, а также внутри большинства операций разрешены подстановки, кроме некоторых (указанных в таблице отдельно).
* Операция <code>qw{}</code> позволяет вернуть список слов. Чаще всего она используется, чтобы инициализировать массив.<source lang="perl">
@arr = qw{ one two three };
# Это аналогично
@arr = ("one", "two", "three");
# и
$arr[0] = "one";
$arr[1] = "two";
$arr[2] = "three";
</source>Обратите внимание, что внутри <code>qw{}</code> запятые ставить не нужно: разделителями являются пробелы.
* Об остальных операциях будет рассказано далее по ходу изложения.
=== Операция Here-docs ===
В Perl перекочевала Here-docs операция, придуманная для командной оболочки Bourne Shell, которая позволяет инициализировать переменные многострочными строками, либо передавать их на вход некоторой операции, записывая строки прямо в исходном файле программы. Как и в Bourne Shell она записывается так
<source lang="perl">
$str = <<EOF;
строка 1
строка 2
строка 3
EOF
print <<END;
Hello, World!
END
</source>
Здесь после знака <code><<</code> вводится так называемое стоп-слово. Далее записывается многострочная строка. Интерпретатор будет искать стоп-слово, для того чтобы понять где заканчивается эта строка. Стоп-слово может быть любым строковым литералом. В наших примерах стоп-словами были <code>EOF</code> и <code>END</code>.
За стоп-словом обычно следует ставить символ точки с запятой, так как Here-docs является простым оператором, конец которого находится после ввода стоп-слова. Пропуск точки с запятой является очень частой ошибкой. Тем не менее, если оператор Here-docs является единственным или последним в текущем блоке, точку с запятой можно опускать.
По умолчанию многострочная строка записывается так, как будто она находится в двойных кавычках. Таким образом, в ней будут разрешаться все подстановки и управляющие последовательности. Вы можете явно обозначить, как должен поступать интерпретатор, если будете использовать соответствующие кавычки вокруг стоп-слова.
<source lang="perl">
$var = "Hello";
print <<END;
\t$var, World!
END
# Аналогично предыдущему вызову
print <<"END";
\t$var, World!
END
# С одинарными кавычками переменные перестанут подставляться и
# не будут работать управляющие последовательности
print <<'END';
\t$var, World!
END
</source>
Можно также использовать наклонные кавычки, тогда в Here-docs можно писать процедуры командной оболочки и инициализировать переменные результатом работы этих процедур. Каждая строка в таких процедурах это отдельный ввод команды в оболочку. Следует помнить, что результаты могут зависеть от операционной системы, в которой исполняется Perl.
<source lang="perl">
# Выведет каталоги корневой точки монтирования в *nix системе.
print <<`END`;
ls -l /
END
</source>
Если стоп-слово вводится без кавычек, то нельзя ставить пробелы между <code><<</code> и стоп-словом. Если такое случится, то Perl не распознает такого слова и будет захватывать строки до первой пустой строки в исходном файле.
В одном операторе можно ввести больше одного стоп-слова (когда это удобно), если записать их подряд следующим образом.
<source lang="perl">
print <<"END1".<<'END2';
double quotes
END1
single quotes
END2
</source>
По умолчанию, Here-docs сохраняет все лидирующие пробелы в многострочной строке, а также требует, чтобы стоп-слово всегда находилось в первой колонке. Например,
<source lang="perl">
print <<END;
ONE
TWO
THREE
END
</source>
напечатает
<pre>
ONE
TWO
THREE
</pre>
Такое поведение не всегда удобно, особенно, когда Here-docs вложена в некоторый блок: в этом случае в исходном коде нарушается выравнивание, из-за чего его неудобно читать. Сравните
<source lang="perl">
# Операция размещена в блоке, но мы не можем выровнять отступы как нам хочется, а также
# стоп-слово END должно всегда начинаться с первой колонки.
if (1) {
print <<END;
ONE
TWO
THREE
END
}
</source>
С версии 5.26 для Here-docs была добавлена особая форма (<code><<~</code>), которая вычисляет отступ для многострочной строки не от колонки документа с исходным кодом, а от отступа самого стоп слова. Таким образом, с версии 5.26 тот же фрагмент можно записать с сохранением исходных отступов так:
<source lang="perl">
# С версии 5.26
if (1) {
print <<~END;
ONE
TWO
THREE
END
# ^- отступ для многострочной строки вычисляется от этой колонки.
}
</source>
Операция Here-docs может комбинироваться с другими операциями. Для этого удобно пользоваться мнемоническим приемом, в котором нужно представлять, что очередная строка заменяет собой стоп-слово справа от знака <code><<</code>. Вот несколько примеров:
<source lang="perl">
# Каждая строка подается операции uc, которая переводит символы в верхний регистр.
print uc(<<EOF);
one
two
three
EOF
# Результат:
# ONE
# TWO
# THREE
# Удалит лидирующие пробелы для каждой строки.
print <<'EOF' =~ s/^\s+//mgr;
Line one with leading spaces
Line two with leading spaces
EOF
# Результат:
# Line one with leading spaces
# Line two with leading spaces
# При выводе строк будет приписывать к каждой порядковый номер.
sub numbering {
print ++$counter . ": " . $_ . "\n" for split '\n', $_[0];
}
numbering(<<'EOF');
int main(int argc, char** argv)
{
return 0;
}
EOF
# Результат:
# 1: int main(int argc, char** argv)
# 2: {
# 3: return 0;
# 4: }
# Напечатает между строками промежуточную.
print <<'PART1' . "Middle String\n" . <<'PART2';
This is the first part.
PART1
This is the final part.
PART2
# Результат:
# This is the first part.
# Middle String
# This is the final part.
</source>
== Другие операции ==
=== Диапазон ===
Бинарная операция диапазон <code>..</code> по-разному интерпретируется в скалярном и списковом контекстах.
В списковом контексте операция возвращает список из чисел, начиная с левого операнда и до последнего с шагом 1, если оба операнда являются числами, либо строки, которые будут сгенерированы в лексикографическом порядке. Если операндами являются числа, то левый операнд всегда должен быть меньше правого, иначе операция вернет пустой список. Числа также должны быть всегда целыми (если это не так, то дробная часть будет просто отбрасываться).
Если строка-левый операнд в лексикографическом смысле больше правого, то результат может зависеть от версии интерпретатора <code>perl</code>: в старых версиях по умолчанию просто возвращается левый операнд; в версии 5.32 будет возвращен ряд, начиная с большего операнда и до символа <code>z</code>. Кроме того, нельзя использовать символы из других алфавитов, кроме латинского: для других алфавитов всегда возвращается левый операнд как результат.
<source lang="perl">
(1..5); # 1 2 3 4 5
(-3..5); # -3 -2 -1 0 1 2 3 4 5
("a".."d"); # a b c d
("01".."10"); # 01 02 03 04 05 06 07 08 09 10
("a1".."b2"); # a1 a2 a3 ... a9 b1 b2
</source>
В скалярном контексте диапазон возвращает булево значение и эмулирует поведение операции запятая <code>,</code> из редакторов Sed и AWK (например, конструкция <code>1,3p</code>). Алгоритм поведения может показаться сложным при первом прочтении, тем не менее следует познакомиться с ним:
* Диапазон в скалярном контексте всегда находится в одном из двух состояний, которое может измениться при повторном вычислении. Условно мы назовем их ИСТИНА и ЛОЖЬ.
* Диапазон остается в ЛОЖНОМ состоянии до тех пор, пока ЛОЖНЫМ остается значение левого операнда. Как только он становится ИСТИНОЙ, диапазон переключает состояние в ИСТИНУ и остается в нем, пока правый операнд остается ИСТИННЫМ.
* Если диапазон находился в состоянии ИСТИНА и правый операнд стал ЛОЖНЫМ, то диапазон снова переключается в ЛОЖНОЕ состояние и вся предыдущая логика повторяется заново.
* В ЛОЖНОМ состоянии этот диапазон возвращает пустую строку (или ЛОЖЬ). В состоянии ИСТИНА при каждом повторном вычислении возвращается порядковый номер, начиная с 1.
* В момент перехода из ИСТИНЫ в ЛОЖЬ диапазон возвращает строку, в которой к последнему порядковому номеру добавляется <code>"E0"</code>, например <code>2E0</code>. В ИСТИННОМ состоянии <code>E0</code> будет отбрасываться.
Вот собственно и весь алгоритм. Добавление <code>"E0"</code> при смене состояния эмулирует поведение именно утилиты AWK. В Sed добавление этой строки происходит только на следующем вычислении. Чтобы эмулировать именно поведение Sed, нужно использовать три точки <code>...</code>.
Диапазоны в скалярном контексте в Perl обычно используются при чтении файлов.
=== Операция запятая ===
Операция запятая <code>,</code> является бинарной и ведет себя по-разному в скалярном и списковом контекстах:
* в списковом контексте она является разделителем для элементов в списке;
* в скалярном контексте она сначала вычисляет левый операнд, потом правый и возвращает результат правого операнда как общий результат.
<source lang="perl">
# Так как левый операнд присваивания это переменная на массив, то выражение исполняется в списковом контексте.
# В этом примере запятая разделяет элементы списка, который передается конструктору массива.
@arr = (1, 2);
# Теперь выражение выполняется в скалярном контексте. Оба операнда для запятой являются простыми числами, но
# как результат операции, будет возвращена 2, которая будет присвоена скаляру.
$scalar = (1, 2);
</source>
У запятой есть синоним в виде <code>=></code>, который мы ранее использовали для построения хеш-массивов в конструкторе. В основном этот знак повторяет запятую, но требует чтобы левый операнд всегда имел строковый тип и корректно в него конвертировался.
Операция является излюбленным инструментом трюков интерпретации. Например, следующая конструкция по порядку вызовет все процедуры, при этом в первой печати мы попутно захватываем результат вызова процедуры <code>b</code>, пользуясь тем, что операция используется в списковом контексте, в котором мы можем пользоваться индексацией массива, а во втором случае, выполняя все в скалярном контексте, мы получаем результат процедуры <code>c</code>:
<source lang="perl">
sub a { print "1\n"; "a" }
sub b { print "2\n"; "b" }
sub c { print "3\n"; "c" }
print STDOUT ( &a, &b, &c )[1], "\n";
print STDOUT scalar ( &a, &b, &c ), "\n";
</source>
<pre>
1
2
3
b
1
2
3
c
</pre>
=== Тернарная операция ===
В Perl есть тернарная условная операция, которая есть во многих языках программирования. Она записывается точно также как в Си:
<source lang="perl">
<операнд 1> ? <операнд 2> : <операнд 3>
</source>
Операция работает так. Если <code><операнд 1></code> является ИСТИНОЙ, то возвращается <code><операнд 2></code> как результат тернарной операции, иначе возвращается <code><операнд 3></code>. Результат тернарной операции можно использовать как в левосторонних (''lvalue''), так и в правосторонних выражениях (''rvalue''), главное чтобы они подставлялись в правильном семантическом контексте.
<source lang="perl">
$var = ($num % 3) ? ($var1 += 1) : ($var1 += 2);
(($a % 3) ? ($a += 2) : $a) -= 2; # Пример с lvalue выражением
</source>
=== Ссылочные операции ===
Большую часть времени мы ссылаемся на данные в памяти компьютера через переменные. Однако язык Perl, подобно Си, разрешает ссылаться на данные в памяти с помощью ''указателей'' или ''ссылок'' (в Perl эти термины равнозначны), которые хранят адрес на ячейку в памяти как значение. Ссылочная передача удобна, когда одни и те же данные разделяют множество процедур и при этом не нужно привязываться к конкретным именам или типам в тексте программы. Возможно процедура сама должна разобраться, что хранится в памяти по переданной ссылке и принять решение самостоятельно.
Чтобы получить адрес переменной, нужно перед ее именем написать обратный слеш <code>\</code> (это аналогично операции получения адреса <code>&</code> в языке Си).
<source lang="perl">
# Ссылка на скаляр
$var = 5;
$ptr = \$var; # Теперь здесь хранится адрес $var
# Ссылка на массив
@arr = (1,2,3);
$aptr = \@arr;
# Ссылка на хеш-массив
%hash = (name => "Larry", surname => "Wall");
$hptr = \%hash;
</source>
Обратите внимание, что адреса хранятся в скалярной переменной. Если мы распечатаем значения, то получим примерно следующее:
<source lang="">
SCALAR(0x800075938)
ARRAY(0x8000759b0)
HASH(0x800075aa0)
</source>
Обратите внимание, что в ссылке хранится информация о типе объекта, на который она ссылается и шестнадцатеричный адрес в памяти компьютера.
Чтобы выполнить обратную операцию, т.е. получить данные по ссылке, ее нужно ''разыменовать''. В отличие от Си, в Perl разыменовывание привязано к типу данных, на который она ссылается. Для этого нужно перед именем ссылки поставить тип, на который будет вестись разыменовывание.
<source lang="perl">
# Разыменовываем ссылку на хеш-массив, чтобы передать его функции keys, которая извлечет из него ключи
@keys = keys(%$hptr);
print $$ptr, "\n"; # Разыменовываем указатель на скаляр
</source>
В предыдущем примере с хеш-массивом мы сначала разыменовали ссылку на хеш-массив, указав символ <code>%</code> в начале, а только потом работали с массивом как обычно, передав его функции <code>keys</code>. Perl позволяет также обращаться к данным массивов непосредственно из ссылки (когда это удобно) при помощи операции разыменовывания массивов <code>-></code>.
<source lang="perl">
print $aptr->[0], $hptr->{name}, "\n";
</source>
Подробнее ссылки мы рассмотрим в отдельной главе (''см.'' [[{{Root book name}}/Ссылки|Ссылки]]).
=== Операции связывания ===
Регулярные выражения являются визитной карточкой Perl, который снабжен различными операциями для проверки по ним. Сюда входят поиск (<code>m//</code>), подстановка (<code>s///</code>) и замена (<code>tr///</code>). По умолчанию они работают с переменной <code>$_</code>.
Кроме них есть операции связывания <code>=~</code> и <code>!~</code>, которые выражают соответственно ''совпадает с шаблоном'' и ''не совпадает с шаблоном''.
Регулярные выражения являются очень обширной темой в Perl, поэтому они будут рассмотрены в отдельной главе (''см.'' [[{{Root book name}}/Работа со строками и регулярные выражения|Работа со строками и регулярные выражения]]).
==== Умное связывание ====
Начиная с 5.10.1 и до 5.37.10 в Perl поддерживалась операция умного связывания, которая умеет сравнивать переменные, хранящие разные типы данных. Данная бинарная операция обозначается двумя тильдами <code>~~</code> и изначально была введена для поддержки оператора <code>given..when</code> (в котором она используется неявно), о котором немного рассказано в следующей главе (см. [[Некоторые сведения о Perl 5/Операторы#Given..when|Операторы#Given..when]]).
Эта операция является особенной, так как её контекст определяется не на этапе компиляции, а во время исполнения программы. Для этого она определяет какой тип данных хранится в её операндах и, в зависимости от этих типов, выбирает механизм сравнивания, причем фактические типы в операндах не обязательно должны совпадать, что и делает её «умной». Если в понимании выбранного механизма операнды равны, она возвращает 1, иначе пустую строку <code>""</code>.
В Perl 5.37.10 данная операция была отмечена как неудачный эксперимент и окончательно будет вырезана в 5.42. Здесь мы о ней упоминаем, чтобы предостеречь не использовать её, хотя следует отметить сколько релизов ей удалось пережить.
=== Именованные унарные операции ===
В Perl есть большое число встроенных функций с одним аргументом, которые с точки зрения синтаксического анализатора являются ''унарными именованными операциями''<ref>Слово ''именованный'' подчеркивает тот факт, что у операции есть имя в виде обозначающего слова, при этом они не требуют скобок вокруг аргумента, копируя операции с одним символом.</ref>. Такие операции подобны функциям, но не требуют скобок вокруг параметра, как того требуют простые функции. О том, является ли функция такой операцией, можно узнать из документации. К таким операциям относятся например <code>do</code>, <code>eval</code>, <code>return</code>, <code>goto</code>, <code>sin</code>, <code>sqrt</code> и другие, а также все операции проверок файлов типа <code>-e</code> (файл существует), <code>-w</code> (в файл можно писать) и другие.
Ключевая особенность именованных унарных операций в Perl заключается в их приоритете: они имеют более высокий приоритет, чем большинство бинарных операций, но ниже, чем скобки и операция стрелки (<code>-></code>).
По ходу изложения мы затронем многие из этих операций.
=== Операции ввода/вывода ===
Это операции, которые направлены на вывод результатов в файлы или ввод данных в программу из них. Так, унарную именованную операцию <code>print</code> можно рассматривать как операцию вывода.
Чтобы обеспечить ввод данных в программу из файлов, нужно сначала их открыть и получить специальный программный объект, называемый ''дескриптором файла''. Дескриптор файла может быть получен через операцию в виде угловых скобок <code><дескриптор_файла></code>:
<source lang="perl">
open(DFILE, "net.cfg"); # Открываем файл. Идентификатор дескриптора DFILE может быть произвольным
@content = <DFILE>; # Читаем файл в массив
</source>
Если бы мы открыли файл на запись, то мы можем перенаправить запись в него так
<source lang="perl">
print MYFILE "Hello";
</source>
Тема ввода/вывода является очень обширной, поэтому она будет рассмотрена в отдельной главе (''см.'' [[{{Root book name}}/Ввод и вывод|Ввод и вывод]]).
== Выражения ==
Понимание того, как интерпретатор Perl парсит выражения, поможет вам лучше понять как они должны составляться. ''Выражение'' — это несколько операндов, объединенных одной и более операциями. Операнд в свою очередь также может быть выражением, т.е. одно общее выражение это конгломерат подвыражений.
Синтаксический анализатор Perl, во время разбора выражения, пытается выделить в нем элементарные члены — ''термы''<ref>Если делать сравнение с естественным языком, то терм является тем же самым, что и ''слово'' в предложении.</ref>, а потом соединить их знаками операций, причем он выстраивает операции в некотором порядке, учитывая приоритет.
В Perl термами являются литералы, переменные, выражения в скобках, закавыченная строка символов, а также функции с параметрами в скобках. Только терм может быть операндом. Отметим, что в Perl отсутствуют истинные функции: по существу они являются списковыми, унарными именованными операциями, ведущими себя как функции. Вызывая функцию, мы выполняем списковую или унарную операцию, которая возвращает некоторый результат.
<source lang="perl">
# Следующее выражение демонстрирует списковую операцию. В ней сначала
# вычисляется как терм каждый элемент списка, а затем каждый элемент выводится
# на печать списковой операции.
print 2 + 2 * 25 / 4, ' ', 16; # Напечатает '14.5 16'
# Следующее выражение дает аналогичный результат. Избыточные скобки
# порождают массив, передаваемый операции.
print (2 + 2 * 25 / 4, ' ', 16); # Напечатает '14.5 16'
# Теперь попробуем добавить конкатенацию:
print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 16'
# Обратите внимание, что результат конкатенации потерялся (нет символа '$'
# в выводе). Это произошло потому, что левый операнд конкатенации, как терм
# по приоритету вычисляется первым, т.е. print выводит результат своего терма
# в аргументе, а не результат конкатенации.
# Допустимо
print print (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '14.5 161$'
# Появление единицы объясняется тем, что внутренний print вернул код 1, который
# был преобразован в строку и был объединен конкатенацией
# c '$'. Так как между внутренним и внешним print нет переноса
# строки, получилась такая склеенная строка.
</source>
При синтаксическом разборе выражений и операторов, как термы рассматриваются конструкции <code>do{}</code> и <code>eval{}</code>, вызовы подпрограмм и методов объектов, анонимные конструкторы массивов скаляров и хешей, а также все операции ввода/вывода.
Еще один немаловажный термин это ''контекст операции''. Операции и термы вычисляются в некотором контексте, от которого зависит поведение операции и возвращаемое ей значение. Существует два основных контекста: ''скалярный'' и ''списковый''. В скалярном контексте интерпретатор пытается вывести из терма скаляр, а в списковом контексте — список литералов <code>qw{}</code>. Контекст определяет обычно тип данных левого операнда. Так, если он является скаляром, то операция выполняется в скалярном контексте; если массив скаляров — списковом контексте. Некоторые операции способны распознавать в каком контексте они исполняются и возвращать свой результат в зависимости от него (универсальные операции).
<source lang="perl">
# В списковой операции print перед списком можно указать выходной дескриптор:
print STDOUT (2 + 2 * 25 / 4, ' ', 16) . '$'; # Напечатает '16$'
# Потеря 14.5 Объясняется тем, что левый операнд конкатенации
# находится в скалярном контексте, а в нем операция запятая
# возвращает последний операнд как результат.
# Уберем скобки:
print STDOUT 2 + 2 * 25 / 4, ' ', 16 . '$'; # Напечатает '14.5 16$'
# Теперь каждый элемент списка после вычисления
# передается списковой операции, каждый в скалярном контексте.
</source>
Так как в Perl типизация динамическая<ref>Другими словами, все операции в Perl изначально перегружены.</ref>, понятие контекста операции является аналогом операции ''приведения типов данных'', которая есть в типизированных языках программирования. В большинстве ситуаций Perl знает лучше, в каком контексте вызывать операцию, но иногда приходится ему помогать, используя синтаксические конструкции и специальные функции. Умение предсказывать, в каком контексте выполнится операция в выражении, позволяет программисту Perl придумывать подчас очень запутанные (но с другой стороны короткие) конструкции, которые не могут себе позволить другие языки программирования из-за типизации. Мы рассмотрим некоторые случаи ниже.
<source lang="perl">
# В простом случае левый операнд задает контекст операции.
@arr = ("apple", "pear", "grapes", "banana");
# Левый операнд скаляр, а значит присваивание работает в скалярном контексте.
# В скалярном контексте массивы в правом операнде приводятся к их размеру.
$scalar = @arr;
print "$scalar\n";
# Левый операнд список скаляров, а значит присваивание работает в списковом контексте.
# В списковом контексте присваивание будет копировать элементы из массива в правом операнде
# поэлементно в список в левом операнде. При этом список слева не должен превышать размера массива.
( $element1, $element2, $element3 ) = @arr;
print $element1
. " $element2"
. " $element3\n";
# Если слева будет переменная на массив, операция также будет в списковом контексте,
# и правый массив переопределит значение переменной.
@arr1 = (1..4);
@arr1 = @arr;
print "@arr1\n";
#-------------------------------------------------------------------------------------------------------
# Операция print всегда работает в списковом контексте и все элементы списка пытается интерпретировать
# как строки.
# В стандартной библиотеке Perl есть функция localtime, которая в списковом контексте возвращает
# список из 9 элементов, кодирующих текущую дату.
print localtime; print "\n";
# Выведет примерно такое: 3219121301251120
# Однако, localtime умеет работать в скалярном контексте, в котором выводит дату в формате текущей локали,
# но этот контекст не может использоваться по умолчанию, так как компилятор только знает о том, что
# print умеет работать в списковом контексте.
#
# Этот тот случай, когда мы можем подсказать компилятору выполнить localtime в скалярном контексте,
# чтобы напечатать дату в читаемой форме. Для этого нужно воспользоваться функцией scalar, которая
# принуждает интерпретировать выражение в своем аргументе исключительно в скалярном контексте.
print scalar localtime; print "\n";
# В локали LANG_EN будет печататься в формате "Mon Jan 13 12:32:17 2025".
# Можно поступить проще и привести компилятор к скалярному контексту через другую операцию, которая
# этот контекст использует. Например, операция конкатенации строк всегда работает в скалярном контексте.
# Если компилятор увидит выражение, использующее скалярный контекст, он будет использоваться для всех
# операндов операции. Таким образом, такого же результата мы добиваемся, если напишем так:
print localtime . "\n";
# "Mon Jan 13 12:32:17 2025"
#-------------------------------------------------------------------------------------------------------
# Рассмотрим более сложный пример.
# Пусть дана такая строка:
$word = "111example222";
# Вопрос: как посчитать в ней количество символов цифр и букв и совместить это с печатью в одном компактном выражении?
# Для ответа на вопрос "сколько символов данного класса?" здесь следует использовать регулярные выражения.
# Так как о регулярных выражениях мы будем говорить много позже, возьмите на веру, что регулярное выражение для
# поиска цифр выглядит так '/\d/g', а букв так '/[^\d\s]/g'.
#
# Примечание: '/[^\d\s]/g' не учитывает Unicode символы.
#
# Проблема кроется в том, что регулярные выражения умеют работать и в скалярном, и в списковом контекстах.
# Конкретно нам нужен списковый контекст, в котором операция возвращает массив совпадений по шаблону, который
# мы могли бы преобразовать в размер, тем самым ответив на вопрос "сколько?". Но как нам сказать, чтобы операция
# выполнялась в списковом контексте, а не скалярном, ведь специальной функции типа scalar для этого нет?
#
# Единственный способ сделать это, привести компилятор к этому контексту через другую операцию.
# Существует два распространенных приёма провоцировать списковый контекст:
# * Можно использовать операцию присваивания, в которой левый операнд это пустой массив.
# * Можно использовать операцию разыменовывания ссылки, которая будет работать в контексте типа
# разыменовывания.
# Ниже мы в одном выражении используем оба приема для демонстрационных целей
print "Number of letters: "
. ( () = $word =~ /[a-zA-Z]/g ) . "\n"
. "Number of digits: "
. ( @{ [ $word =~ /\d/g ] } ) . "\n"
;
# Number of letters: 7
# Number of digits: 6
</source>
Не расстраивайтесь, если вы ничего из предыдущих примеров не поняли, так как мы сильно забегаем в них вперед. Эти примеры призваны показать, как сильно выражения завязаны на контекст операции.
Скалярный контекст также можно разделить по типу хранимых данных на ''числовой'', ''строковый'' и ''безразличный''. Perl неспособен заранее распознать этот контекст, поэтому он просто подключает механизм неявного приведения данных, когда скаляры в операндах хранят разные типы.
Еще один специальный скалярный контекст — ''булевый''. Так как в Perl нет булева <code>true</code> или <code>false</code>, Perl анализирует строки и числа в булевом контексте, как это было описано выше: <code>0</code>, <code>"0"</code>, <code>"00"</code>, <code>"0.0"</code> и <code>""</code> это условно ЛОЖЬ, а все остальное ИСТИНА.
Еще одним контекстом является void-контекст, который используется, когда в выражении нет побочных эффектов. Так, вы можете спокойно писать в Perl такие бессмысленные конструкции (они выполняются в void-контексте):
<source lang="perl">
(1,2,3);
$n;
"Hello";
</source>
Этот контекст можно обнаружить, если интерпретатор Perl запущен в режиме вывода предупреждающих сообщений.
Еще один контекст это ''подстановочный контекст'', в котором вычисляются операции заключения в кавычки (кроме одинарных кавычек). В этом контексте подставляются переменные и интерпретируются управляющие последовательности.
Ниже для справки представлены приоритеты операций.
{|class="wikitable" style="width:100%;"
|-
! Приоритет
! Операция
! Сочетаемость
|-
|style="width:10%;"|1
|Вычисление термов и левосторонних списковых операций
|style="width:20%;"|Слева направо
|-
|2
|<code><nowiki>-></nowiki></code>
|Слева направо
|-
|3
|<code><nowiki>++</nowiki></code> <code><nowiki>--</nowiki></code>
|Не сочетаются
|-
|4
|<code><nowiki>**</nowiki></code>
|Справа налево
|-
|5
|<code><nowiki>!</nowiki></code> <code><nowiki>~</nowiki></code> <code><nowiki>\</nowiki></code> унарный <code><nowiki>+</nowiki></code> унарный <code><nowiki>-</nowiki></code>
|Справа налево
|-
|6
|<code><nowiki>=~</nowiki></code> <code><nowiki>!=</nowiki></code>
|Слева направо
|-
|7
|<code><nowiki>*</nowiki></code> <code><nowiki>/</nowiki></code> <code><nowiki>%</nowiki></code> <code><nowiki>x</nowiki></code>
|Слева направо
|-
|8
|<code><nowiki>+</nowiki></code> <code><nowiki>-</nowiki></code> <code><nowiki>.</nowiki></code>
|Слева направо
|-
|9
|<code><nowiki><<</nowiki></code> <code><nowiki>>></nowiki></code>
|Слева направо
|-
|10
|Именованные унарные операции
|Не сочетаются
|-
|11
|<code><nowiki><</nowiki></code> <code><nowiki>></nowiki></code> <code><nowiki><=</nowiki></code> <code><nowiki>>=</nowiki></code> <code><nowiki>lt</nowiki></code> <code><nowiki>gt</nowiki></code> <code><nowiki>le</nowiki></code> <code><nowiki>ge</nowiki></code>
|Не сочетаются
|-
|12
|<code><nowiki>==</nowiki></code> <code><nowiki>!=</nowiki></code> <code><nowiki><=></nowiki></code> <code><nowiki>eq</nowiki></code> <code><nowiki>ne</nowiki></code> <code><nowiki>cmp</nowiki></code>
|Не сочетаются
|-
|13
|<code><nowiki>&</nowiki></code>
|Слева направо
|-
|14
|<code><nowiki>|</nowiki></code> <code><nowiki>^</nowiki></code>
|Слева направо
|-
|15
|<code><nowiki>&&</nowiki></code>
|Слева направо
|-
|16
|<code><nowiki>||</nowiki></code>
|Слева направо
|-
|17
|<code><nowiki>..</nowiki></code> <code><nowiki>...</nowiki></code>
|Не сочетаются
|-
|18
|<code><nowiki>?:</nowiki></code>
|Справа налево
|-
|19
|<code><nowiki>=</nowiki></code> <code><nowiki>**=</nowiki></code> <code><nowiki>+=</nowiki></code> <code><nowiki>-=</nowiki></code> <code><nowiki>.=</nowiki></code> <code><nowiki>*=</nowiki></code> <code><nowiki>/=</nowiki></code> <code><nowiki>%=</nowiki></code> <code><nowiki>x=</nowiki></code> <code><nowiki>&=</nowiki></code> <code><nowiki>|=</nowiki></code> <code><nowiki>^=</nowiki></code> <code><nowiki><<=</nowiki></code> <code><nowiki>>>=</nowiki></code> <code><nowiki>&&=</nowiki></code> <code><nowiki>||=</nowiki></code>
|Справа налево
|-
|20
|<code><nowiki>,</nowiki></code> <code><nowiki>=></nowiki></code>
|Слева направо
|-
|21
|Правосторонние списковые операции
|Не сочетаются
|-
|22
|<code><nowiki>not</nowiki></code>
|Справа налево
|-
|23
|<code><nowiki>and</nowiki></code>
|Слева направо
|-
|24
|<code><nowiki>or</nowiki></code> <code><nowiki>xor</nowiki></code>
|Слева направо
|}
* В данной таблице чем выше приоритет операции и чем левее она стоит (в большинстве случаев), тем раньше она выполняется.
* Приоритет списковой операции зависит от ее места в выражении и может изменяться от наивысшего к наименьшему. Все операции в выражении, стоящие слева от списковой, имеют наивысший приоритет по отношению к ней (смотрите 21 приоритет).
* Свойство сочетаемости означает, что операции одного приоритета могут быть в одном выражении, причем порядок их разрешения может быть слева направо или справа налево.
* Скобки позволяют изменить порядок вычисления, определяемому в обычных условиях приоритетом и сочетаемостью. Скобки всегда наделяют выражение в них наивысшим приоритетом.
== Примечания ==
{{примечания}}
27wz7dgii2ou6ea8k8zhy0i4h3fdn99
АОН/Образование
0
35859
269934
2026-09-02T23:26:59Z
Leksey
3027
Создание страницы в минимальном стабе
269934
wikitext
text/x-wiki
{{АОН Страница}}
'''{{SUBPAGENAME}}''' — данная страница описывает связь правил, регулирующих образование в России и летное обучение. За счет этой связи сами подходы к летному обучению усложняются (как для организаторов этого обучения так и для будущих пилотов).
==Подготовка частных пилотов самолетов и вертолетов ==
Подготовка частных пилотов самолетов и вертолетов относится к т.н. ДПО. В рамках ДПО гражданина обладающего одной специализацией учат на пилота. ДПО не может быть короче 250 часов даже если нет смысла столько учить.
==Подготовка пилотов СВС и планеристов==
С 2026 года подготовка пилотов СВС и планеристов больше не ДПО, а относится к профессиональной
==См. также==
* [[АОН/АУЦ]]
* [[АОН/АУЦ/Поступление]]
== Ссылки ==
==Примечания==
{{Примечания}}
{{АОН}}
gc2d5yq9r94ubv6p6palqq2jt9txprh
269936
269934
2026-09-02T23:36:11Z
Leksey
3027
Про ДПО и прочую шляпу
269936
wikitext
text/x-wiki
{{АОН Страница}}
'''{{SUBPAGENAME}}''' — данная страница описывает связь правил, регулирующих образование в России и летное обучение. За счет этой связи сами подходы к летному обучению усложняются (как для организаторов этого обучения так и для будущих пилотов).
==Подготовка частных пилотов самолетов и вертолетов (в рамках ДПО) ==
Подготовка частных пилотов самолетов и вертолетов относится к т.н. ДПО. В рамках ДПО гражданина обладающего одной специализацией учат на пилота. ДПО не может быть короче 250 часов даже если нет смысла столько учить.
==Подготовка пилотов СВС и планеристов (в рамках ПО)==
С 2026 года подготовка пилотов СВС и планеристов больше не ДПО, а относится к "профессиональному обучению". Профессиональная подготовка не накладывает таких требований как ДПО и снимает ограничения. К профессиональной подготовке также относится обучение на мотоциклетный или автомобильные права.
Чтобы прийти к этому выводу (что СВС и планеристы это не ДПО) надо обратиться к , где есть перечень профессий в Приказе Министерства просвещения № 534.
{| class="wikitable"
| valign="top" |022(1)
| valign="top" |Пилот сверхлегкого воздушного судна
| valign="top" |-
| valign="top" |-
|-
| valign="top" |4022(2)
| valign="top" |Пилот планера
| valign="top" |-
| valign="top" |-
|-
| valign="top" |4022(3)
| valign="top" |Внешний пилот
| valign="top" |-
| valign="top" |-
|}
== Новое название Минобр ==
Теперь Минобр носит название Министерство просвещения. Именно этот орган отвечает за общее образование (среднее), среднее профессиональное образование и соответствующее дополнительное профессиональное образование, профессиональное обучение, дополнительное образования детей и взрослых.
В этом перечне пилотов касается прежде всего
а) профессиональное обучение
б) дополнительное профессиональное образование (ДПО).
==См. также==
* [[АОН/АУЦ|АУЦ]]
* [[АОН/АУЦ/Поступление|Требования к кандидатам на обучение в АУЦ]]
== Ссылки ==
* [https://ivo.garant.ru/#/document/407525777/paragraph/1/doclist/186/1/0/0/%D0%9F%D1%80%D0%B8%D0%BA%D0%B0%D0%B7%20%D0%9C%D0%B8%D0%BD%D0%BF%D1%80%D0%BE%D1%81%D0%B2%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%A0%D0%A4%20%D0%BE%D1%82%2014.07.2023%20N%20534:0 Приказ Министерства просвещения РФ от 14 июля 2023 г. № 534 "Об утверждении Перечня профессий рабочих, должностей служащих, по которым осуществляется профессиональное обучение"]
==Примечания==
{{Примечания}}
{{АОН}}
d0aghxj4fv41c707wzz44q5798oigx6
АОН/ДПО
0
35860
269935
2026-09-02T23:27:00Z
Leksey
3027
#REDIRECT[[АОН/Образование]]
269935
wikitext
text/x-wiki
#REDIRECT[[АОН/Образование]]
5tz3ohw9jen7mxcdjtgbdj6i2xhlx22
АОН/Минобр
0
35861
269937
2026-09-02T23:45:24Z
Leksey
3027
#REDIRECT[[АОН/Образование]]
269937
wikitext
text/x-wiki
#REDIRECT[[АОН/Образование]]
5tz3ohw9jen7mxcdjtgbdj6i2xhlx22