Викиучебник 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.8 first-letter Медиа Служебная Обсуждение Участник Обсуждение участника Викиучебник Обсуждение Викиучебника Файл Обсуждение файла MediaWiki Обсуждение MediaWiki Шаблон Обсуждение шаблона Справка Обсуждение справки Категория Обсуждение категории Полка Обсуждение полки Импортировано Обсуждение импортированного Рецепт Обсуждение рецепта Задача Обсуждение задачи TimedText TimedText talk Модуль Обсуждение модуля Event Event talk АОН/Госкорпорация 0 27783 269195 257169 2026-06-24T16:59:59Z Leksey 3027 Оформление 269195 wikitext text/x-wiki {{АОН страница}} {{Википедия|Госкорпорация по ОрВД}} Российская организация федерального уровня, которая обеспечивает диспетчерское обслуживание ([[АОН/ДО|ДО]]) воздушных судов в российском воздушном пространстве (являясь т.н. [[АОН/Поставщик аэронавигационного обслуживания|ПАО]]). Все [[АОН/Диспетчер ОВД|диспетчера ОВД]] являются её сотрудниками или подчинены ей. Сама организация является подведомственной Росавиации, но напрямую ей не управляется.<ref>Хотя такие действия все же происходят</ref> Госкорпорация имеет достаточно сложную структуру, но строит разобраться для более эффективного взаимодействия, как при подготовке полётов, так и непосредственно при их выполнении. == Главный центр или сокращенно ГЦ == ГЦ (главный центр) структурное подразделение, имеющееся в составе Госкорпорации. Помимо прочего выдаёт разрешение иностранным ВС (на основании поданного плана полёта), но на полёт по трассам [[АОН/Полёт иностранного ВС|- Полёт иностранного ВС]]. Англоязычное название ГЦ - MATMC. Адрес ГЦ в АФТН - UUUWZDZX На него отправляются сообщения FPL при нерегулярных рейсах. Т.е. при полетах АОН. Из-за границы и внутри России. Про адресацию FPL написано вот тут [[АОН/План_полета/Адресация]]. == Жалобы == Отзывы на работу диспетчеров, а также работы наземной инфраструктуры, частным пилотам стоит направлять на электронную почту ans_quality@gkovd.ru<ref>[https://gkovd.ru/ano/ gkovd.ru/ano/]</ref> ==Оплата услуг== С некоторых пор оплачивать услуги АНО стало проще и для этого можно использовать Личный кабинет<ref>[https://anc.gkovd.ru/ Личный кабинет по оплате аэронавигационных сборов]</ref>. До этого оплачивать приходилось бумажные счета, которые приходили на адрес регистрации. == См. также == *[[АОН/Полёт иностранного ВС|Полёт иностранного ВС]] *[[АОН/План_полета/Адресация|План полета - адресация]] == Примечания == {{Примечания}} {{АОН}} 528p0dvgszzt8vnyozt3w9tpabwk92e 269202 269195 2026-06-25T03:36:44Z Leksey 3027 /* См. также */ +поставщику аэронавигационного обслуживания 269202 wikitext text/x-wiki {{АОН страница}} {{Википедия|Госкорпорация по ОрВД}} Российская организация федерального уровня, которая обеспечивает диспетчерское обслуживание ([[АОН/ДО|ДО]]) воздушных судов в российском воздушном пространстве (являясь т.н. [[АОН/Поставщик аэронавигационного обслуживания|ПАО]]). Все [[АОН/Диспетчер ОВД|диспетчера ОВД]] являются её сотрудниками или подчинены ей. Сама организация является подведомственной Росавиации, но напрямую ей не управляется.<ref>Хотя такие действия все же происходят</ref> Госкорпорация имеет достаточно сложную структуру, но строит разобраться для более эффективного взаимодействия, как при подготовке полётов, так и непосредственно при их выполнении. == Главный центр или сокращенно ГЦ == ГЦ (главный центр) структурное подразделение, имеющееся в составе Госкорпорации. Помимо прочего выдаёт разрешение иностранным ВС (на основании поданного плана полёта), но на полёт по трассам [[АОН/Полёт иностранного ВС|- Полёт иностранного ВС]]. Англоязычное название ГЦ - MATMC. Адрес ГЦ в АФТН - UUUWZDZX На него отправляются сообщения FPL при нерегулярных рейсах. Т.е. при полетах АОН. Из-за границы и внутри России. Про адресацию FPL написано вот тут [[АОН/План_полета/Адресация]]. == Жалобы == Отзывы на работу диспетчеров, а также работы наземной инфраструктуры, частным пилотам стоит направлять на электронную почту ans_quality@gkovd.ru<ref>[https://gkovd.ru/ano/ gkovd.ru/ano/]</ref> ==Оплата услуг== С некоторых пор оплачивать услуги АНО стало проще и для этого можно использовать Личный кабинет<ref>[https://anc.gkovd.ru/ Личный кабинет по оплате аэронавигационных сборов]</ref>. До этого оплачивать приходилось бумажные счета, которые приходили на адрес регистрации. == См. также == *[[АОН/Полёт иностранного ВС|Полёт иностранного ВС]] *[[АОН/План_полета/Адресация|План полета - адресация]] *[[АОН/Поставщик аэронавигационного обслуживания|Поставщик аэронавигационного обслуживания]] == Примечания == {{Примечания}} {{АОН}} b6a81hti03m7fxey0517j3avdmyqbpa 269205 269202 2026-06-25T03:50:51Z Leksey 3027 /* Главный центр или сокращенно ГЦ */ 269205 wikitext text/x-wiki {{АОН страница}} {{Википедия|Госкорпорация по ОрВД}} Российская организация федерального уровня, которая обеспечивает диспетчерское обслуживание ([[АОН/ДО|ДО]]) воздушных судов в российском воздушном пространстве (являясь т.н. [[АОН/Поставщик аэронавигационного обслуживания|ПАО]]). Все [[АОН/Диспетчер ОВД|диспетчера ОВД]] являются её сотрудниками или подчинены ей. Сама организация является подведомственной Росавиации, но напрямую ей не управляется.<ref>Хотя такие действия все же происходят</ref> Госкорпорация имеет достаточно сложную структуру, но строит разобраться для более эффективного взаимодействия, как при подготовке полётов, так и непосредственно при их выполнении. == Главный центр или сокращенно ГЦ == ГЦ (главный центр) структурное подразделение, имеющееся в составе Госкорпорации. Помимо прочего выдаёт разрешение иностранным ВС (на основании поданного плана полёта), но на полёт по трассам [[АОН/Полёт иностранного ВС|- Полёт иностранного ВС]]. Англоязычное название ГЦ - MATMC. Адрес ГЦ в АФТН - UUUWZDZX На него отправляются сообщения FPL при нерегулярных рейсах. Т.е. при полетах АОН. Из-за границы и внутри России. Про адресацию FPL написано вот тут - [[АОН/План_полета/Адресация|адресация]]. == Жалобы == Отзывы на работу диспетчеров, а также работы наземной инфраструктуры, частным пилотам стоит направлять на электронную почту ans_quality@gkovd.ru<ref>[https://gkovd.ru/ano/ gkovd.ru/ano/]</ref> ==Оплата услуг== С некоторых пор оплачивать услуги АНО стало проще и для служит Личный кабинет<ref>[https://anc.gkovd.ru/ Личный кабинет по оплате аэронавигационных сборов]</ref>. До этого оплачивать приходилось бумажные счета, которые приходили на адрес регистрации. == См. также == *[[АОН/Полёт иностранного ВС|Полёт иностранного ВС]] *[[АОН/План_полета/Адресация|План полета - адресация]] *[[АОН/Поставщик аэронавигационного обслуживания|Поставщик аэронавигационного обслуживания]] == Примечания == {{Примечания}} {{АОН}} 2c36q2uov96n29ht1cvqn38ng8j1eut АОН/ОВД 0 27828 269196 269136 2026-06-24T17:00:38Z Leksey 3027 Оформление 269196 wikitext text/x-wiki '''ОВД''' в контексте авиации означает "обслуживание воздушного движения". Для обозначений различного рода диспетчерских центров используется термин "органы ОВД". ОВД осуществляется путём передачи органами ОВД экипажам ВС так называемых диспетчерских указаний, разрешений, рекомендаций и информации <ref>[http://ivo.garant.ru/#/document/70117238 п. 3.18 - ФАП по ОрВД N 293 "Организация воздушного движения] </ref> ==Виды ОВД== Их существует три вида: *Диспетчерское обслуживание (сокращенно ДО) - [[АОН/ДО|ДО]] *Полетно-информационное обслуживание (сокращенно ПИО) - [[АОН/ПИО|ПИО]] *Аварийное оповещение (сокращенно АО) [[АОН/АО|- АО]] ==Кто занимается== Обслуживание воздушного движения в России возложено на [[АОН/Госкорпорация|Госкорпорацию]]. ==Терминология== Также есть похожее словосочетание "Организация воздушного движения" и для нее используется аббревиатура ОрВД. Организация воздушного движения это глобальное понятие, куда входит непосредственно и ОВД. ==Кто осуществляет ОВД== Обслуживание воздушного движения осуществляют органы диспетчерского обслуживания. {{Основная статья|АОН/ДО/Органы}} == См. также == *[[АОН/Госкорпорация]] ==Ссылки == *[http://ivo.garant.ru/#/document/70117238 ФАП по ОрВД N 293 "Организация воздушного движения] ==Примечания== {{Примечания}} {{АОН}} 4c8azcwjfc3b9zaar7yod0kzhnr7aqg Некоторые сведения о Perl 5/Объектно-ориентированное программирование в Perl 0 30406 269199 264178 2026-06-24T19:05:13Z ScriptMaster 15708 /* Другие методы */ 269199 wikitext text/x-wiki {{Навигация учебника}} В языке Perl не существовало специального синтаксиса для описания классов до версии 5.38. Для их реализации использовались существующие синтаксические конструкции. ''Класс в Perl'' представляет собой пакет, а функции в пакете представляют его ''методы''. Таблица символов класса представляет его содержимое — список полей и методов. ''Объект в Perl'' — это ссылка на экземпляр класса, которая строится с помощью специальной функции класса — ''конструктора''. Функция <code>ref()</code> возвращает для ссылки на объект не стандартный идентификатор типа <code>SCALAR</code>, <code>ARRAY</code> и др., а возвращает имя класса, которому принадлежит объект<ref>По внутренней терминологии такая ссылка называется благословлённой ({{lang-en|blessed}})</ref>. В общих чертах, конструктор любого класса для конструирования ссылки на объект должен вызывать функцию <source lang="perl"> bless <ссылка> [, <имя-класса>] </source> которая «''благословляет''» ({{lang-en|to bless}} — благословлять) ссылку на то, чтобы принадлежать к классу, указанному вторым аргументом. Если имя класса опущено, то используется имя текущего пакета. Именно ссылка, возвращаемая этим методом, должна возвращаться из конструктора. После того как объект построен, к нему можно обращаться через квалифицированное имя <code>$<имя-класса>::<ссылка></code>. Наследование в Perl еще сильнее отличается от того, что есть в других языках программирования. В Perl наследуются только методы. Наследование данных реализует сам программист. Наследование методов реализовано так: с каждым пакетом ассоциирован свой массив <code>@ISA</code>, в котором хранится список базовых классов пакета. Если внутри класса вызывается метод, которого нет в текущем классе, то интерпретатор в поисках метода просматривает по порядку методы каждого класса массива <code>@ISA</code>. Затем просматривается предопределенный класс <code>UNIVERSAL</code>. Если и после этого метод не был найден, то классы снова просматриваются в том же порядке в поисках процедуры <code>AUTOLOAD</code>. Если такая находится, то вызывается она вместо отсутствующего метода. В эту процедуру будут переданы все параметры, а в переменной <code>$AUTOLOAD</code> будет хранится имя вызываемого метода. == Объявление класса == === Конструктор === Конструктор класса является простой функцией пакета. Она может иметь любое имя, но на практике обычно используют идентификатор <code>new</code>, как устоявшееся имя для любого конструктора. Самый простой конструктор имеет следующий вид: <source lang="perl"> package MyClass; sub new { my $class = shift; # Передаем конструктору имя класса my $self = {}; # Конструируем ссылку bless $self, $class; # Конструируем объект return $self; # Возвращаем ссылку } # --------------------------------------------- package main; # Конструируем объект $object = MyClass::new("MyClass"); </source> Для примера мы объявляем класс и конструируем его экземпляр в одном исходном файле. Здесь мы это делаем только для демонстрации: на практике классы следует объявлять в отдельных исходных файлах и подключать их через директиву <code>use()</code>. Имя ссылки <code>$self</code> является устоявшимся, и мы настоятельно рекомендуем использовать ее в своих проектах. Данный пример демонстрирует пустой бесполезный класс, в котором нет полей, так как мы конструируем объект из ссылки на пустой хеш. На практике обычно в конструктор передается массив или другой хеш, которым инициализируется ссылка <code>$self</code>. В общем случае не обязательно использовать именно хеш, но его преимущество в том, что мы можем использовать произвольные ключи. Также мы можем передавать массив значений, которыми затем мы можем инициализировать анонимный хеш. Другими словами, подходы здесь могут быть самые разные. На практике конструктор класса обычно имеет следующий вид: <source lang="perl"> sub new { # Предполагается, что $init это ссылка на структуру с инициирующими данными my ( $class, $init ) = @_; $class = ref( $class ) || $class; # Чтобы иметь возможность взять имя по ссылке. # Примечание: если имя класса не будет передано, будет взято имя ближайшего пространства имен. my $self = {}; bless $self, $class; # ... Код инициализирующий поля в ссылке $self через $init ... # ... return $self; } </source> === Деструктор === Когда ссылка на объект выходит из зоны видимости, она удаляется по нулевому счетчику ссылок. Программист может контролировать действия, которые исполнятся перед очисткой ссылки, через ''деструктор'' объекта. Деструктор объекта нужен, чтобы корректно завершить жизненный цикл объекта, например, закрыть открытые объектом файловые дескрипторы. Деструктор всегда вызывается автоматически. Деструктор всегда должен быть объявлен внутри класса и всегда должен иметь имя <code>DESTROY</code>. Также деструктор должен принимать аргумент в виде ссылки на удаляемый объект. Деструктор является необязательным методом объекта, так как ссылка на удаляемый объект в любом случае будет очищаться правильно. Данный метод позволяет вам лучше контролировать этот процесс. Деструктор не вызывает автоматически другие деструкторы, другими словами, если ваш класс наследует другой, деструктор базового класса нужно вызвать явно в деструкторе дочернего. Ниже приведен простой пример деструктора. <source lang="perl"> package MyClass; sub new { my $class = shift; # Передаем конструктору имя класса my $self = {}; # Конструируем ссылку bless $self, $class; # Конструируем объект return $self; # Возвращаем ссылку } sub DESTROY { my $name = ref($_[0]); print "Call destructor to delete an instance of " . $name . " class\n"; } # --------------------------------------------- package main; { my $object = MyClass::new("MyClass"); } # Деструктор объекта в блоке вызывается в этой точке. $object = MyClass::new("MyClass"); # Деструктор объекта, созданного в области видимости пакета main удаляется перед завершением программы. </source> Если вызвать программу, можно убедиться, что деструктор был вызван для каждого из двух объектов: <source lang=""> Call destructor to delete an instance of MyClass class Call destructor to delete an instance of MyClass class </source> === Другие методы === Методы в Perl могут вызываться от имени конкретного объекта или от имени всего класса. В последнем случае такие методы называются ''статическими''. Статическому методу в первом аргументе всегда передается имя класса, а не статическому — ссылка на объект. Типичным примером статического метода является конструктор (именно поэтому мы ожидаем в первом аргументе имя класса). В Perl методы выполняются в пространстве имен того пакета, в котором они были определены, а не в том, в котором они вызываются. Именно поэтому многие статические методы игнорируют свой первый аргумент. Не статические методы выполняют самую разную работу. Обычно эти действия направлены на инкапсулированные в объекте данные. В Perl полностью инкапсулировать данные невозможно, так как вы можете при желании исправлять их прямо через ссылку на объект (если конечно знаете как он устроен). На практике всегда следует изменять инкапсулированные данные через методы класса, игнорируя то, что сам объект является простой ссылкой, так как в этом и есть ООП подход. <source lang="perl"> package Person; $classCounter = 0; # Конструктор sub new { my ($class, $data) = @_; # Передаем конструктору имя класса вместе с ссылкой на структуру my $self = $data; bless $self, $class; # Конструируем класс $classCounter++; return $self; } # Деструктор sub DESTROY { my $name = ref($_[0]); print "Call destructor to delete an instance of " . $name . " class:\n"; while ( my($k,$v) = each %{$_[0]} ) { print "\t$k => $v\n"; } $classCounter--; } # Простой сеттер, устанавливающий имя персоны sub setName { my ($self, $data) = @_; $self->{'name'} = $data; return $self; } # Простой геттер, возвращающий поле 'name' sub getName { my $self = shift; return $self->{'name'}; } sub setSurname { my ($self, $data) = @_; $self->{'surname'} = $data; return $self; } sub getSurname { my $self = shift; return $self->{'surname'}; } # Данный метод может вызываться как статический. sub show { my $self = shift; my @keys = @_ ? @_ : sort keys %$self; foreach $key (@keys) { print "\t$key => $self->{$key}\n"; } return $self; } # Чисто статический метод sub count { my $class = shift; print "Counter of $class: $classCounter\n"; } # --------------------------------------------- package main; # Конструируем два объекта $person1 = Person::new(Person, {name => 'Larry', surname => 'Wall'}); $person2 = Person::new(Person, {}); print "-- 1 --\n"; $person1->show(); # вызов метода print "-- 2 --\n"; $person1->show('name'); # печатаем одно поле структуры $person1->setName('Garry'); # меняем поле структуры print "-- 3 --\n"; Person::show($person1); # вызываем статический метод print "-- 4 --\n"; $person1->count(); # вызываем метод, считающий объекты Person::count(); Person::count('Person'); print "-- 5 --\n"; $person2->setName('Homer'); # вызываем сеттеры $person2->setSurname('Simpson'); $person2->show(); print "-- 6 --\n"; print STDOUT $person1->getName(), "\n"; # вызываем геттер print "-- 7 --\n"; # строим объект, чтобы увеличить счетчик { local $person1 = Person::new(Person, {}); Person::count('Person'); # 3 } Person::count('Person'); # 2 </source> Результат работы программы <source lang="bash"> -- 1 -- name => Larry surname => Wall -- 2 -- name => Larry -- 3 -- name => Garry surname => Wall -- 4 -- Counter of Person=HASH(0x55cc685f3470): 2 # Статический метод, вызванный от имени объекта Counter of : 2 # Без аргумента метод ведет себя как статический Counter of Person: 2 -- 5 -- name => Homer surname => Simpson -- 6 -- Garry -- 7 -- Counter of Person: 3 # Нарастили счетчик ссылок Call destructor to delete an instance of Person class: Counter of Person: 2 # Объект был удален - счетчик уменьшился Call destructor to delete an instance of Person class: surname => Wall name => Garry Call destructor to delete an instance of Person class: surname => Simpson name => Home </source> Методы класса можно вызывать двумя способами. Первый способ имеет вид <source lang="perl"> <имя-метода> <имя-класса-или-объект>, <параметры>; print "-- 1 --\n"; $person = Person::new 'Person', {name => "Larry", surname => 'Wall'}; Person::show $person, 'name', 'surname'; print "-- 2 --\n"; # В общем случае метод может распечатать любую ссылку Person::show {name => 'Dennis', surname => 'Ritchie', born => '1941'}; print "-- 3 --\n"; Person::show Person::new 'Person', {name => 'Dennis', surname => 'Ritchie', born => '1941'}; print "-- 4 --\n"; print STDOUT Person::getSurname Person::new 'Person', {name => 'Dennis', surname => 'Ritchie', born => '1941'}; </source> <source lang="bash"> -- 1 -- name => Larry surname => Wall -- 2 -- born => 1941 name => Dennis surname => Ritchie -- 3 -- born => 1941 name => Dennis surname => Ritchie Call destructor to delete an instance of Person class: surname => Ritchie born => 1941 name => Dennis -- 4 -- RitchieCall destructor to delete an instance of Person class: name => Dennis surname => Ritchie born => 1941 Call destructor to delete an instance of Person class: surname => Wall name => Larry </source> Данная форма представляет собой вызов функции, вложенный в другой вызов. Второй формой мы пользовались в самом первом примере этого раздела: <source lang="perl"> <класс-или-объект>-><метод>(<параметры>) $person = Person->new({name => 'Larry', surname => 'Wall', born => '1954'}); print "-- 1 --\n"; $person->show('name', 'born'); print "-- 2 --\n"; Person->new({name => 'Larry', surname => 'Wall', born => '1954'})->show('name', 'surname'); </source> <source lang="bash"> -- 1 -- name => Larry born => 1954 -- 2 -- name => Larry surname => Wall Call destructor to delete an instance of Person class: name => Larry born => 1954 surname => Wall Call destructor to delete an instance of Person class: name => Larry born => 1954 surname => Wall </source> Вторая форма требует обязательного заключения аргументов в скобки. В большинстве случаев вторая форма предпочтительнее в смысле читаемости текста программы, так как оператор разыменовывания наглядно отделяет части выражения. При использовании первой формы могут возникнуть трудноуловимые ошибки, особенно когда действует область видимости, в которой существует функция с тем же именем, что и у метода класса. На практике, особенно для такого языка, как Perl, расточительно создавать полный набор геттеров и сеттеров. В большинстве случаев достаточно написать универсальный сеттер, либо объединить геттер и сеттер одного поля в одном методе: <source lang="perl"> package Class; use strict; sub new { my $self = {}; bless $self; return $self; } sub name { my ($self, $value) = @_; return $self->{'name'} = $value if defined $value; return $self->{'name'} || ""; } # --------------------------------------------- package main; my $obj = Class->new; $obj->name("Larry"); print $obj->name, "\n"; </source> === Наследование === Для реализации механизма наследования в Perl используется специальный массив <code>@ISA</code>, в который помещаются родительские классы слева направо от ближайшего к дальнему предку. Если интерпретатор встречает вызов метода, которого нет в данном классе, то он сначала пройдется по классам из <code>@ISA</code> в поисках метода. Если вам нужно вызвать метод родительского класса из дочернего класса, вы можете воспользоваться квалифицированным вызовом метода. Если вам нужен метод ближайшего предка, вы можете использовать имя псевдокласса <code>SUPER</code> (но только из методов потомка). <source lang="perl"> package Parent; sub new { my ($class, $data) = @_; my $self = $data; bless $self, $class; return $self; } sub DESTROY { print "Call destructor to delete an instance of 'Parent' class\n"; } sub set { my ($self, $data) = @_; for $i (keys %$data) { $self->{$i} = $data->{$i}; } return $self; } sub show { my $self = shift; my @keys = @_ ? @_ : sort keys %$self; print "Parent:\n"; foreach $key (@keys) { print "\t$key => $self->{$key}\n"; } return $self; } sub AUTOLOAD { print "Package " . __PACKAGE__ . ": there is not '$AUTOLOAD' method\n"; } package Child; @ISA = (Parent); sub new { my ($class, $data) = @_; my $self = Parent->new($data); $self->{"born"} = 0; bless $self, $class; return $self; } sub DESTROY { my $self = shift; print "Call destructor to delete an instance of '" . ref($self) . "' class\n"; # Вызываем деструктор родительского класса $self->SUPER::DESTROY; } # Переопределяем метод sub show { my $self = shift; print "Child:\n"; $self->SUPER::show(); return $self; } package main; $person = Child->new({name => 'Larry', surname => 'Wall'}); print "-- 1 --\n"; $person->show(); print "-- 2 --\n"; $person->set({born => 1954}); $person->show(); print "-- 3 --\n"; $person->Parent::show(); print "-- 4 --\n"; $person->undefinedMethod; </source> <source lang="bash"> -- 1 -- Child: Parent: born => 0 name => Larry surname => Wall -- 2 -- Child: Parent: born => 1954 name => Larry surname => Wall -- 3 -- Parent: born => 1954 name => Larry surname => Wall -- 4 -- Package Parent: there is not 'Child::undefinedMethod' method Call destructor to delete an instance of 'Child' class Call destructor to delete an instance of 'Parent' class </source> В данном примере мы имеем два класса <code>Parent</code> и <code>Child</code>. Родительский класс объявляет два метода: метод <code>Parent::set()</code> нужен, чтобы устанавливать поля структуры класса; метод <code>Parent::show()</code> распечатывает все поля структуры класса. Дочерний класс наследует метод <code>Parent::set()</code>, поэтому мы можем вызывать его без квалификатора типа, и переопределяет метод <code>Parent::show()</code>. Также дочерний класс в своем конструкторе вводит поле <code>born</code>, которое по умолчанию инициализируется нулевым значением. Обратите внимание, как мы определяем методы дочернего класса: все они вводят небольшие изменения в алгоритм, но все равно вызывают методы родительского класса. На практике не всегда нужно поступать так, но конструкторы и деструкторы по логике вещей всегда должны обращаться к конструкторам и деструкторам предков, причем конструирование должно происходить сверху вниз (от дальнего предка к последнему потомку), а для деструкторов снизу вверх (от дальнего потомка к дальнему предку). В последнем операторе мы пытаемся вызывать несуществующий метод из объекта дочернего класса. Так как в родительском классе объявлен метод <code>AUTOLOAD</code>, то будет вызван именно он. ==== Множественное наследование ==== Язык Perl поддерживает множественное наследование за счет массива <code>@ISA</code>. В этом массиве вы можете разместить любое количество родительских классов, методы которых будут просматриваться, если реализации соответствующего метода нет в потомке. Ниже показано наследование, при котором класс <code>Child</code> объединяет в себе свойства двух родительских классов. <source lang="perl"> package Parent1; sub new { my $class = shift; bless {}, $class; } sub greet { return "Parent1::greet\n"; } package Parent2; sub new { my $class = shift; bless {}, $class; } sub talk { return "Parent2::talk\n"; } sub greet { return "Parent2::greet\n"; } package Child; @ISA = qw(Parent1 Parent2); sub new { my $class = shift; return bless {}, $class; } package main; my $child = Child->new; print $child->greet(); # Вызовется реализация Parent1, так как этот родитель в списке наследования раньше. print $child->talk(); # Вызовется реализация Parent2. print $child->Parent2::greet(); # Вызов реализации Parent2. </source> <pre> Parent1::greet Parent2::talk Parent2::greet </pre> Множественное наследование обычно следует избегать, но если всё-таки приходится его использовать, следует быть предельно внимательным и учитывать вероятность получения [[w:Ромбовидное наследование|ромбовидного наследования]], при котором есть вероятность унаследовать один и тот же метод у двух и более родителей. Для языка Perl это не является особой проблемой, так как родители проверяются всегда в одном порядке, но это может приводить к логической ошибке в программе и неправильному поведению объекта класса потомка. Если необходимо изменить порядок проверки родительских классов, следует использовать модуль [https://perldoc.perl.org/mro MRO]. Множественное наследование можно избежать, если использовать [[w:Агрегирование (программирование)|агрегирование]] вместо наследования. Также можно использовать интерфейс ролей класса <code>UNIVERSAL</code>, в котором определен метод <code>DOES()</code>. Переопределив этот метод в потомках, вы можете управлять вызовами в зависимости от того, какую роль берет на себя конкретная реализация. === Использование таблицы символов для генерации методов на ходу === Некоторые стандартные методы, такие как геттеры и сеттеры, могут быть сгенерированы по контексту самой структуры класса. Для этого мы можем пройтись по полям структуры класса и сгенерировать из имен полей стандартные методы устанавливающие и возвращающие значения. Внимательно изучите следующий пример. <source lang="perl"> package Person; sub new { my ($class, $params) = @_; my $self = {}; # В этом цикле мы генерируем методы через псевдонимы и анонимные функции. # Геттеры и сеттеры генерируются каждый раз при вызове конструктора, что является недостатком подхода. # Чтобы исправить недостаток, нужно добавить проверку на существование метода, но в этом примере мы это опустим. # Для геттеров используются имена get_<имя-поля>, а для сеттеров – set_<имя-поля>. for my $key (sort keys %$params) { *{__PACKAGE__ . '::' . "get_$key"} = sub { my $self = shift; return $self->{$key}; }; *{__PACKAGE__ . '::' . "set_$key"} = sub { my ($self, $data) = @_; $self->{$key} = $data; return $self; }; $self->{$key} = $params->{$key}; } bless $self, $class; return $self; } package main; $person = Person->new({name => 'Larry', surname => 'Wall'}); print $person->get_name() . " " . $person->get_surname, "\n"; $person->set_name('Garry'); print $person->get_name() . " " . $person->get_surname, "\n"; </source> <source lang=""> Larry Wall Garry Wall </source> В примере выше мы генерируем как будто бы пустой класс. На самом деле он генерирует свои методы на ходу, а именно при вызове конструктора. Для этого он для каждого поля хеша, передаваемого в конструктор, генерирует анонимную функцию, к которой можно обращаться по ссылке. Для геттеров имя ссылки будет <code>get_<имя-поля></code>, а для сеттеров — <code>set_<имя-поля></code>. Этот пример демонстрирует, что в принципе в Perl можно создавать очень сложные абстрактные классы, но вероятнее всего вам не захочется этим заниматься. == Шаблон модуля, несущего в себе класс == <source lang="perl"> package MyClass; use strict; # Для чистоты кода require Exporter; # Для функции import() our @ISA = qw{ Exporter }; # Добавьте родительские классы, если нужно our @EXPORT = qw{ new method1 method2 }; # Добавьте публичные методы класса sub new { my ($class, $data) = @_; my $self = $data; bless $self, $class; return $self; } sub method1 { my ($self, $data) = @_; # ... } sub method2 { my $self = shift; my $data = shift; # Примечание: shift возвращает очередной аргумент и сдвигает строку аргументов налево # ... } sub private { my $self = shift; # ... } 1; __END__ Документация </source> == Пакет UNIVERSAL == Все классы в <code>@ISA</code> наследуют пакет с именем <code>UNIVERSAL</code>, который предоставляет несколько методов, реализующих рефлексию классов в Perl. Его можно сравнить с классом <code>Object</code> в языке Java, но <code>UNIVERSAL</code> устроен намного проще. Пакет <code>UNIVERSAL</code> объявляет всего 4 метода, которыми модули могут пользоваться для исследования возможностей текущего программного окружения. === Метод VERSION === Возвращает значение переменной <code>$VERSION</code> пакета (модуля). Переменная <code>$VERSION</code> может быть объявлена непосредственно в пакете, но рекомендуется инициализировать её в директиве <code>package</code>, так как это более наглядно. <source lang="perl"> package SomePackage 1.0; # Пакет версии 1.0 sub new { my $self = {}; bless $self; return $self; } #------------------------------------------------------------------ package main; print "SomePackage version: " . SomePackage->VERSION, "\n"; my $obj = SomePackage->new; # Номер версии можно вернуть и из экземпляра. print "SomePackage version: " . $obj->VERSION, "\n"; </source> <pre> SomePackage version: 1.0 SomePackage version: 1.0 </pre> У метода есть необязательный аргумент, в котором вы можете передать ожидаемый номер версии. Если фактический номер версии оказывается меньше запрашиваемого, функция выбрасывает исключение. Предполагается, что более старшие версии не совместимы с младшими. <source lang="perl"> package SomePackage 1.0; # Пакет версии 1.0 ... sub new { my $self = {}; bless $self; return $self; } #------------------------------------------------------------------ package main; my $obj = SomePackage->new; eval { # ... но запрашиваем 2.0 $obj->VERSION(2.0); }; print $@ if $@; </source> <pre> SomePackage version 2 required--this is only version 1.0 at .\universal.pl line 18. </pre> === Метод DOES === Проверяет, есть ли в цепочке наследования текущего класса класс с именем, передаваемом в аргументе. Если есть, то возвращает ИСТИНУ, иначе ЛОЖЬ. <source lang="perl"> package Parent; # ... package Child; use parent qw{Parent}; sub new { my $self = {}; bless $self; return $self; } package main; print "The Child inherits the Parent\n" if Child->DOES('Parent'); print "The Child does not inherit the AnotherClass\n" unless Child->DOES('AnotherClass'); my $obj = Child->new; print "The Child inherits the Parent\n" if $obj->DOES('Parent'); </source> <pre> The Child inherits the Parent The Child does not inherit the AnotherClass The Child inherits the Parent </pre> === Метод can === Проверяет, существует ли метод с указанным именем в цепочке наследования или нет. Если метод существует, то метод возвращает ссылку на него, иначе возвращается ЛОЖЬ. <source lang="perl"> package Parent; # ... sub parentMethod { print "Parent method\n"; } package Child; use parent qw{Parent}; sub new { my $self = {}; bless $self; return $self; } sub childMethod { print "Child method\n"; } package main; print "The Parent has 'parentMethod'\n" if ref Parent->can('parentMethod') eq 'CODE'; my $obj = Child->new; print "The Child has 'parentMethod'\n" if ref $obj->can('parentMethod') eq 'CODE'; # Получаемый метод можно вызвать, но нужно передать экземпляр класса в первом аргументе. $obj->can('childMethod')->($obj); </source> <pre> The Parent has 'parentMethod' The Child has 'parentMethod' Child method </pre> === Метод isa === По умолчанию <code>isa</code> и <code>DOES</code> работают абсолютно одинаково, однако метод <code>isa</code> задумывался, чтобы проверить утверждение ''инстанцирована ли ссылка от переданного в аргументе класса ИЛИ этот класс есть в цепочке наследования''<ref>Сравните с операцией <code>instanceof</code> в языке Java.</ref>, а <code>DOES</code> призвана определить, ''может ли этот класс выполнять данный класс задач''. Некоторые CPAN модули переопределяют внутри себя <code>DOES</code>, поэтому он может возвращать отличные от <code>isa</code> ответы. == Подведение итогов == Описанный выше подход к ООП не является единственным. Существуют модули CPAN, которые пытаются улучшить эту систему, обогащая описанные выше скудные конструкции (например, модуль Moose). Такие модули, как правило, используются в больших проектах. Однако, как бы там ни было, CPAN модули могут требовать загрузки множества зависимостей и могут замедлять скорость исполнения программы из-за своей внутренней реализации. В маленьких проектах достаточно встроенного в Perl ООП. == Perl 5.38: class или попытка сдвинуться с мёртвой точки == Очень долго тема ООП в Perl стояла на месте, но с версии 5.38 разработчики интерпретатора решили сделать новый шаг в этом направлении и ввели новое ключевое слово в язык — <code>class</code>. Дело в том, что у старого подхода есть много недостатков, что не позволительно для ООП парадигмы, в частности: * По сути у классов нет понятия ''поля класса'', а то что есть не обладает полноценной инкапсуляцией. Кроме того, никак не реализуется наследование полей в дочерние классы. Все эти моменты идут на откуп программисту, который, конечно, в этом разберётся, но вероятно напишет не очень понятный код. * Методы класса внешне не выглядят инкапсулировано: не запрещено разбросать их по пакетам, пользуясь уловками интерпретации, а также никакой приватности реализовать в них нельзя. Кроме того, нужно всегда помнить, что в первый аргумент любого метода передаётся ссылка на объект, что вынуждает писать одинаковую первую строку для всех методов, что очень утомляет (сравните с автоматической ссылкой <code>this</code> в C++). Реализация <code>class</code> на момент 2025 года ещё относительно сырая и ей стоит пользоваться осторожно, но можно потихоньку присматриваться. Если вы хотите использовать эту возможность, то обновите интерпретатор минимум до версии 5.40, потому что первые реализации имеют достаточно серьезные ошибки. === Объявление класса === Так как на момент версии 5.40 возможность является экспериментальной, её нужно включать так: <source lang="perl"> use feature 'class'; # Отключает предупреждения об экспериментальной возможности, которые засоряют вывод. no warnings 'experimental::class'; </source> В синтаксисе чувствуется, что разработчики вдохновлялись языком C++, так как синтаксис очень похож: <source lang="perl"> class <имя> <версия> [: <атрибуты-наследования>] [{ <блок-класса> }] # Также класс может быть объявлен, но не определен. class <имя> [<версия>]; </source> Внутренне слово <code>class</code> работает как <code>package</code>, но дополнительно оно генерирует конструктор по умолчанию для пакета. Номер версии для класса используется, когда используется наследование и у класса есть несколько реализаций, чтобы не сломать обратную совместимость. Номер версии может быть опущен, если атрибуты наследования не используются. Для <code>class</code> было введено слово <code>__CLASS__</code>, которое автоматически разворачивается в имя класса в пределах его определения. Им следует пользоваться для манипуляций с элементами класса, если они происходят до инстанцирования объекта. Вот примеры объявления класса <code>Person</code>: <source lang="perl"> class Person 1.000 { # ... } class WithoutVersion { # ... } # Объявление класса class Empty; </source> === Поля класса === Чтобы объявить поле в классе, используется ключевое слово <code>field</code>. Это ключевое слово инкапсулирует поле в классе и гарантирует, что каждый инстанцированный объект класса получит свой набор полей в памяти программы. Полный синтаксис поля такой: <source lang="perl"> class ClassName { field <переменная> [: <атрибуты>] [= <выражение-вычисляющее-инициализирующее-значение>]; } </source> Например, <source lang="perl"> class Person 1.000 { field $name = "Larry"; field $surname = "Wall"; field $born; } </source> Данный синтаксис позволяет присвоить значение полю до вызова конструктора, а также, когда значение для поля не передается через конструктор. Что-то похожее есть в языке Java, но здесь вы можете использовать полноценное Perl-выражение справа от присваивания. === Методы класса === Чтобы объявить метод, используется ключевое слово <code>method</code>. Методы уже становятся очень похожими на методы языка C++, в частности вы можете обращаться к полям класса не используя ссылку на объект. С помощью ключевого слова можно объявлять удобные сигнатуры: <source lang="perl"> class ClassName { method [<имя-метода>] [<сигнатура-метода>] { <блок-метода> } } </source> Сигнатуры в новой реализации поддерживают значения по умолчанию. Внутри методов вы можете обращаться к экземпляру объекта по ссылке <code>$self</code>. Ниже представлены примеры. <source lang="perl"> use feature 'class'; no warnings 'experimental::class'; class Person 1.000 { field $name = "Larry"; field $surname = "Wall"; field $born; method printAsString { print __CLASS__ . ":" . " Name: " . $name . ", Surname: " . $surname . ", Born: " . $born . "\n" ; } # Метод с сигнатурой method setBorn($value = "unknown") { $born = $value; } # Метод, возвращающий анонимый метод. method jsonBuilder { return method { use JSON::PP; my $obj = { name => $name, surname => $surname, born => $born, }; return JSON::PP->new->ascii->pretty->allow_nonref->encode($obj); }; } method asJson { # # В этой системе пока еще не все идеально: анонимным методам нужно # ссылку на объект передавать вручную. # return $self->jsonBuilder->($self); } } my $o = Person->new; $o->printAsString; $o->setBorn("27.09.1954"); $o->printAsString; print $o->asJson, "\n"; </source> <pre> Person: Name: Larry, Surname: Wall, Born: Person: Name: Larry, Surname: Wall, Born: 27.09.1954 { "name" : "Larry", "surname" : "Wall", "born" : "27.09.1954" } </pre> === Атрибуты === Атрибуты призваны для установки правил видимости и доступа к различным частям класса как внутри него самого, так и из классов-потомков. В документации говорится, что атрибуты будут: * у класса; * у полей класса; * у методов (но на момент 5.40 ни одного атрибута для методов ещё не реализовано). ==== Атрибуты класса ==== У класса на момент 5.40 поддерживаются следующие атрибуты: * <code>:isa</code> — атрибут устанавливает отношение между классами типа ''родитель-потомок''. В атрибуте вписывается имя класса родителя и опционально минимальная необходимая версия этого класса (если фактическая версия родителя меньше запрашиваемого, исполнение прерывается). Так как у полей всегда лексическая видимость, эта система умеет реализовывать только приватное наследование полей в терминах языка C++. Если класс ещё не был загружен в пространство текущего пакета, неявно вызывается процедура <code>require</code>.<source lang="perl"> class Parent 2.0 { field $some_field = "Hello, World!"; method hello { print $some_field, "\n"; } method getField { return $some_field; } } class Child :isa(Parent) { method child_method { print "Child: "; $self->SUPER::hello(); # НЕЛЬЗЯ: приватное наследование. #print $some_field, "\n"; # Доступ к полям родителя можно получить # только через методы. print "some_field: " . $self->SUPER::getField . "\n"; } } my $o = Child->new; $o->child_method; # Сработает защита, так как версия родителя меньше запрашиваемой. class AnotherChild :isa(Parent 3.0) { # ... } </source> ==== Атрибуты полей ==== У полей на момент 5.42 поддерживаются следующие атрибуты: * <code>:param</code> — указывает на то, что начальное значение для этого поля берется из именованного параметра конструктора. По умолчанию, именованный параметр конструктора и имя поля совпадают, но в параметрах атрибута можно указать другое имя, если так удобно или того требует ситуация. Вы можете оставить выражение, которое присваивает значение по умолчанию, но если его нет, значение всегда должно присваиваться через конструктор.<source lang="perl"> class ModernPerson { field $name :param(person_name); field $surname :param; field $born :param = "unknown"; method printAsString { print __CLASS__ . ":" . " Name: " . $name . ", Surname: " . $surname . ", Born: " . $born . "\n" ; } } my $o1 = ModernPerson->new(person_name => "Larry", surname => "Wall"); my $o2 = ModernPerson->new(person_name => "Larry", surname => "Wall", born => "27.09.1954"); $o1->printAsString; # ModernPerson: Name: Larry, Surname: Wall, Born: unknown $o2->printAsString; # ModernPerson: Name: Larry, Surname: Wall, Born: 27.09.1954 </source> * <code>:reader</code> — генерирует для поля геттер. По умолчанию имя геттера совпадает с именем поля, но в параметре атрибута можно указать другое имя.<source lang="perl"> class ModernPerson { field $name :param(person_name) :reader(get_name); field $surname :param :reader; field $born :param = "unknown"; } my $o = ModernPerson->new(person_name => "Larry", surname => "Wall", born => "27.09.1954"); print $o->get_name . " " . $o->surname, "\n"; </source> * <code>:writer</code> — генерирует для поля сеттер. На момент версии 5.42 сеттер можно сгенерировать только для скалярного поля, иначе будет сгенерирована ошибка времени компиляции. По умолчанию имя сеттера генерируется по шаблону <code>set_<имя-поля></code>, но в параметре атрибута можно указать другое имя. Все генерируемые автоматически сеттеры возвращают ссылку на объект, поэтому сеттеры можно вызывать по цепочке.<source lang="perl"> class ModernPerson { field $name :writer; field $surname :writer(surname); field $born :writer = "unknown"; method printAsString { print __CLASS__ . ":" . " Name: " . $name . ", Surname: " . $surname . ", Born: " . $born . "\n" ; } } my $o = ModernPerson->new; $o->set_name("Larry"); # Вызов по цепочке. $o->surname("Wall")->set_born("27.09.1954"); $o->printAsString; </source> === Жизненный цикл объекта класса === Так как <code>class</code> это по сути замаскированный <code>package</code>, то всё что было сказано ранее в классическом подходе Perl к ООП справедливо и здесь в отношении жизненного цикла. Правда есть одно отличие: для <code>class</code> был добавлен специальный именованный блок <code>ADJUST</code>, который семантически очень похож на <code>BEGIN</code>, но из него вам доступна ссылка на инстанцированный экземпляр класса, т.е. этот блок выполняется сразу за конструктором при конструировании каждого нового объекта. Вы можете указать сколько угодно таких блоков и они будут выполняться в порядке появления. Ниже показаны этапы жизненного цикла на конкретном примере. <source lang="perl"> class First { ADJUST { print 'Parent ADJUST 1 of ' . ref $self, "\n"; } ADJUST { print 'Parent ADJUST 2 of ' . ref $self, "\n"; } sub DESTROY { my $self = shift; print "Called parent destructor for an instance of " . ref $self, "\n"; } } class Second :isa(First) { ADJUST { print 'ADJUST 1 of ' . ref $self, "\n"; } ADJUST { print 'ADJUST 2 of ' . ref $self, "\n"; } sub DESTROY { my $self = shift; $self->SUPER::DESTROY; print "Called destructor for an instance of " . ref $self, "\n"; } } { my $o = First->new; } print "--------------------------------------------\n"; { my $o = Second->new; } </source> <pre> Parent ADJUST 1 of First Parent ADJUST 2 of First Called parent destructor for an instance of First -------------------------------------------- Parent ADJUST 1 of Second Parent ADJUST 2 of Second ADJUST 1 of Second ADJUST 2 of Second Called parent destructor for an instance of Second Called destructor for an instance of Second </pre> Мы можем видеть, что при наследовании конструкторы и блоки <code>ADJUST</code> вызываются автоматически в порядке от родителя к потомку. Как и раньше, для деструкторов мы должны закладывать вызов деструктора родителя в деструкторе потомка . == Перегрузка операций классов == В этом разделе мы разберем одну из центральных функций ООП в Perl — ''перегрузка операций''. Многие программисты ругают Perl за очень сложный синтаксис ООП, но на самом деле Perl по большому счёту не нужна ООП парадигма в той форме, в которой она есть, например, в языке Java. Perl в первую очередь всегда решал задачи системного администрирования, где скорее важны минимализм и практичность, так как у системных администраторов время на решение входящих запросов очень ограничено. В Perl существует по меньшей мере два подхода перегружать операции: * '''Перегрузка связыванием'''. Данный тип перегрузки опирается на функцию <code>tie()</code>, которая привязывает объект-делегат к переменной определенного типа данных и переопределяет операции для этой переменной (привязывается к ней). Данный тип перегрузки концентрируется на этапах жизненного цикла объекта и перегружает через ''конструктор'', ''деструктор'', операции ''присваивания'' и ''копирования''. * '''Перегрузка операций класса'''. Реализуется через модуль <code>overload</code> и позволяет перегружать знаки операций для выражений, в которых используется объект некоторого класса. === Перегрузка связыванием === Идея этого подхода кроется в желании обобщить работу с хранилищами данных по отношению к типам данных языка Perl. В языке Perl можно любую структуру представить комбинацией стандартных типов языка (скаляр, массив и хеш-массив). С другой стороны, сценарии Perl обычно конечной целью ставят перед собой прочитать данные из некоторого источника и записать данные в некоторый источник. Обычно протокол работы с определенным типом хранилища данных всегда строго определен. Отсюда возникает желание одни и те же данные записывать в разные типы хранилищ одинаковым, с точки зрения интерфейса, способом. Здесь и появляется функция <code>tie()</code>, которая привязывает реализацию-делегат к экземпляру стандартного типа данных. При этом делегат внутри себя полностью управляет протоколом работы с хранилищем, а наружу для клиентского кода перегружает операции ''присваивание'' и ''извлечение'' через знак операции <code>=</code>. Данный подход впервые появился в Perl 5 и поддерживает привязывание для: * скаляров (<code>Tie::Scalar</code>), * массивов (<code>Tie::Array</code>), * хеш-массивов (<code>Tie::Hash</code>), * файловых дескрипторов (<code>Tie::Handle</code>). Основу интерфейса <code>tie()</code> составляют следующие функции: <source lang="perl"> # Строит экземпляр делегата и привязывает его к переменной. tie <переменная-к-которой-привязывается-делегат>, <класс-делегата>, <список-аргументов-для-конструктора-делегата> # Отвязывает делегата от переменной и удаляет его. untie <переменная-к-которой-привязывается-делегат> # Возвращает ссылку на экземпляр делегата. tied <переменная-к-которой-привязывается-делегат> </source> Состав интерфейсных методов для каждого типа данных отличается, так как каждый тип в чём-то специфичен, но в общем каждый тип представляет следующие методы: * <code>TIEHASH</code>, <code>TIEARRAY</code>, <code>TIEHANDLE</code> и <code>TIESCALAR</code> — конструктор делегата соответственно для хеш-массива, массива, дескриптора и скаляра. Сигнатура конструктора для всех типов одинаковая: в первом аргументе передается имя пакета делегата, а в последующих список аргументов для него. * <code>FETCH</code> — определен для всех типов, кроме дескриптора. Вызывается каждый раз, когда связанная переменная участвует в выражении как правосторонний операнд, т.е. значение из источника извлекается. * <code>STORE</code> — определен для всех типов, кроме дескриптора. Вызывается каждый раз, когда связанная переменная участвует в выражении как левосторонний операнд, т.е. значение в источник записывается. * <code>DESTROY</code> — деструктор делегата. Вам не обязательно переопределять все методы, предоставляемые интерфейсом, так как тривиальные реализации всегда есть в соответствующем пакете <code>Tie::</code>. ==== Связывание скаляров ==== Основы работы с tie-интерфейсом проще всего показать на скаляре, так как для скаляра определено самое малое количество методов: * <code>TIESCALAR</code> — конструктор делегата. * <code>FETCH</code> — метод, извлекающий данные из привязанного скаляра. * <code>STORE</code> — метод, записывающий данные в привязанный скаляр. * <code>DESTROY</code> — деструктор делегата. Ниже показан небольшой пример делегирующего класса. <source lang="perl"> use strict; package TieScalarExample; # Конструктор делегата. sub TIESCALAR { my ($class, @args) = @_; print "Tie " . __PACKAGE__ . ": " . (join ", ", @args), "\n"; return bless {}, $class; } # Метод извлекающий данные. sub FETCH { my ($self) = @_; "To fetch a record"; } # Метод записывающий данные. sub STORE { my ($self, $value) = @_; print "Storing a value: $value", "\n"; } # Деструктор делегата. sub DESTROY { my ($self) = @_; print "Before untie()"; } #---------------------------------------------------------- package main; our ($aScalar, $anotherScalar); tie $aScalar, 'TieScalarExample', 1, 2, 3; # Вызывается TieScalarExample::TIESCALAR $anotherScalar = $aScalar; # Вызывается TieScalarExample::FETCH print "$anotherScalar", "\n"; # 'To fetch a record' $aScalar = 15; # Вызывается TieScalarExample::STORE untie $aScalar; # Вызывается TieScalarExample::DESTROY </source> <pre> Tie TieScalarExample: 1, 2, 3 To fetch a record Storing a value: 15 Before untie() </pre> В комментариях к примеру показано, в какие моменты вызываются интерфейсные методы. В этом примере мы привязываем делегата <code>TieScalarExample</code> к скаляру <code>$aScalar</code>, после чего любая операция <code>=</code> будет переводиться на методы привязанного делегата до момента вызова <code>untie()</code> в зависимости от того, в левом или правом операнде операции участвует связанный скаляр. Перед отвязыванием делегата вызывается деструктор делегата. В предыдущем примере делегат по сути ничего не делает, но на практике часто за делегатом скаляра стоит реальное хранилище данных, а сам делегат выступает его драйвером. Ниже представлен конкретный пример, в котором делегат логирует все значения, которые проходят через скаляр в любом направлении. <source lang="perl"> #!/usr/bin/perl use strict; package ScalarTracker; use IO::File; # # $log - имя файла, в который производится логирование. # $var - начальное значение. # sub TIESCALAR { my $class = shift; my $log = shift; my $var = shift || "(undefined)"; my $fh = new IO::File ">> $log" or die "Cannot open $log: $!\n"; return bless {FH => $fh, VAL => 0, VAR => $var}, $class; } sub FETCH { my $self = shift; my ($package, $filename, $line) = caller(); my $fh = $self->{FH}; print $fh "package $package, ", "$filename line $line FETCHED $self->{VAR}\n"; return $self->{VAL}; } sub STORE { my $self = shift; my $var = shift; my $fh = $self->{FH}; my ($package, $filename, $line) = caller(); print $fh "package $package, ", "$filename line $line changed $self->{VAR} to $var\n"; $self->{VAL} = $var; } sub DESTROY { undef %{$_[0]}; } #---------------------------------------------------------- package main; our $aFruit; tie $aFruit, 'ScalarTracker', './log.txt', 'none'; for (qw{ Apple Pear Grapes Banana }) { print "Printing: " . ($aFruit = $_), "\n"; } untie $aFruit; </source> Если вы откроете файл лога, то увидите следующие строки: <pre> package main, ./tiescalar.pl line 54 changed none to Apple package main, ./tiescalar.pl line 54 FETCHED none package main, ./tiescalar.pl line 54 changed none to Pear package main, ./tiescalar.pl line 54 FETCHED none package main, ./tiescalar.pl line 54 changed none to Grapes package main, ./tiescalar.pl line 54 FETCHED none package main, ./tiescalar.pl line 54 changed none to Banana package main, ./tiescalar.pl line 54 FETCHED none </pre> а на консоли <source lang="bash"> $ ./tiescalar.pl Printing: Apple Printing: Pear Printing: Grapes Printing: Banana </source> Вы можете увидеть странную запись в логах <pre> package main, ./tiescalar.pl line 54 FETCHED none </pre> Она говорит о том, что списковая операция <code>print</code> внутренне тоже обращается к скаляру. ==== Связывание массивов ==== Интерфейс для массивов уже заметно богаче: * <code>TIEARRAY</code> и <code>DESTROY</code> — конструктор и деструктор делегата. * <code>STORE</code> — вызывается, когда по конкретному индексу записывается значение в массив. * <code>FETCH</code> — вызывается, когда по конкретному индексу извлекается значение из массива. * <code>DELETE</code> — вызывается, когда удаляется элемент по индексу. * <code>CLEAR</code> — вызывается для операции <code>delete</code> для всего массива. * <code>PUSH</code>, <code>POP</code>, <code>SHIFT</code>, <code>UNSHIFT</code>, <code>SPLICE</code> — переопределяет стандартные одноименные операции над массивом. * <code>EXISTS</code> — вызывается, когда проверяется существование значение по индексу. * <code>EXTEND</code> — предполагается, что метод расширяет занимаемую память под массив на указанное число позиций. * <code>FETCHSIZE</code> — предполагается, что возвращает реальное число элементов в массиве. * <code>STORESIZE</code> — предполагается, что подравнивает размер массива под указанное значение. Если часть элементов выходит за пределы, они должны быть удалены. Если новый размер больше текущего, то новые значения должны быть проинициализированы <code>undef</code>. Ниже показано, в какие моменты вызываются методы интерфейса. <source lang="perl"> use strict; package TieArrayExample; sub TIEARRAY { my ($class, @argv) = @_; return bless {}, $class; } sub FETCH { my ($self, $value) = @_; } sub FETCHSIZE { my ($self) = @_; } sub STORE { my ($self, $index, $value) = @_; } sub STORESIZE { my ($self, $count) = @_; } sub EXISTS { my ($self, $key) = @_; } sub DELETE { my ($self, $key) = @_; } # Опциональные методы sub CLEAR { my ($self) = @_; } sub PUSH { my ($self, @list) = @_; } sub POP { my ($self) = @_; } sub SHIFT { my ($self) = @_; } sub UNSHIFT { my ($self, @list) = @_; } sub SPLICE { my ($self, $offset, $length, @list) = @_; } sub EXTEND { my ($self, $count) = @_; } sub DESTROY { my ($self) = @_; } #---------------------------------------------------------- package main; our @array; tie @array, 'TieArrayExample'; # TIEARRAY my $value = $array[0]; # FETCH $array[0] = 1; # STORE scalar @array; # FETCHSIZE exists $array[0]; # EXISTS push @array, "1"; # PUSH pop @array; # POP shift @array; # SHIFT unshift @array, 1; # UNSHIFT splice @array, 1; # SPLICE и STORESIZE delete $array[0]; # DELETE undef @array; # CLEAR untie @array; # DESTROY </source> ==== Связывание хеш-массивов ==== Для хеш-массивов определены следующие методы: * <code>TIEHASH</code> и <code>DESTOROY</code> — конструктор и деструктор делегата. * <code>STORE</code> — сохраняет значение в хеш-массив по ключу. * <code>FETCH</code> — извлекает значение из хеш-массива по ключу. * <code>FIRSTKEY</code> — возвращает самый первый ключ в массиве. * <code>NEXTKEY</code> — возвращает следующий ключ. Используется для перебора элементов хеш-массивов через <code>each()</code>. * <code>EXISTS</code> — проверяет существование ключа. * <code>DELETE</code> — удаляет значение из хеш-массива по ключу. * <code>CLEAR</code> — удаляет хеш-массив целиком. * <code>SCALAR</code> — вызывается, когда к хеш-массиву происходит обращение в скалярном контексте. Ниже показано, в какие моменты вызываются методы интерфейса. <source lang="perl"> use strict; package TieHashExample; sub TIEHASH { my ($class, @argv) = @_; return bless {}, $class; } sub DESTROY { my ($self) = @_; } sub FETCH { my ($self, $key) = @_; } sub STORE { my ($self, $key, $value) = @_; } sub FIRSTKEY { my ($self) = @_; } sub NEXTKEY { my ($self, $lastkey) = @_; undef; } sub EXISTS { my ($self, $key) = @_; } sub DELETE { my ($self, $key) = @_; } sub CLEAR { my ($self) = @_; } sub SCALAR { my ($self) = @_; } #---------------------------------------------------------- package main; our %hash; tie %hash, 'TieHashExample'; # TIEHASH $hash{'aaaa'} = 1; # STORE my $v = $hash{'aaaa'}; # FETCH exists $hash{'aaaa'}; # EXISTS delete $hash{'aaaa'}; # DELETE undef %hash; # CLEAR scalar %hash; # SCALAR while ( my ($k,$v) = each %hash ) { # FIRSTKEY и NEXTKEY } untie %hash; # DESTROY </source> Следует дать пояснения по методам <code>FIRSTKEY</code> и <code>NEXTKEY</code>. Эти методы реализуют подобие итератора. Метод <code>FIRSTKEY</code> вызывается всегда, когда хеш-массив передается функции <code>keys()</code> или <code>each()</code>. Этот метод должен сбросить состояние итератора, если оно не начальное, и вернуть начало хеш-массива. Метод <code>NEXTKEY</code> возвращает последующий элемент, вычисленный от последнего, переданного аргументом <code>$lastkey</code>. Если возвращать нечего, нужно вернуть значение <code>undef</code>, которое говорит, что элементы закончились. ==== Связывание дескрипторов ==== Связывание для файловых дескрипторов в Perl очень полезный функционал в *nix системах, так как в них через файловые дескрипторы можно получить доступ к любой части системы. Например, можно подключаться к блочным или символьным устройствам через специальные файлы и работать с ними через операции файловых дескрипторов из кода Perl. Так как через дескрипторы ходят символьные потоки, в них нет методов <code>STORE</code> и <code>FETCH</code>, а используются методы <code>WRITE</code> и <code>READ</code>. Интерфейс поддерживает следующие методы: * <code>TIEHANDLE</code> и <code>DESTROY</code> — конструктор и деструктор делегата. * <code>WRITE</code> — записывает указанное число байтов с некоторого начального адреса и некоторого смещения от его начала. * <code>READ</code> — читает указанное число байтов с некоторого начального адреса и некоторого смещения от его начала. * <code>READLINE</code> — читает несколько байтов до некоторого разделителя. * <code>GETC</code> — читает очередной символ в символьном потоке. * <code>PRINT</code> — печатает значения в переданном списке. * <code>PRINTF</code> — печатает значения в списке с использованием форматной строки. * <code>OPEN</code> — открывает или переоткрывает файловый дескриптор. * <code>CLOSE</code> — закрывает файловый дескриптор. * <code>BINMODE</code> — указывает на то, что читаемый поток представляет собой поток байтов, а не отдельных символов (другими словами, включает бинарный режим чтения). * <code>EOF</code> — возвращает истину, если поток закончился. * <code>TELL</code> — возвращает текущую позицию в файле. * <code>SEEK</code> — передвигает позицию в файле относительно некоторой опорной точки. Ниже показано, в какие моменты вызываются методы интерфейса. <source lang="perl"> use strict; package TieHandleExample; sub TIEHANDLE { my ($class, @argv) = @_; return bless {}, $class; } sub WRITE { my ($self, $address, $length, $offset) = @_; } sub READ { my ($self, $address, $length, $offset) = @_; } sub READLINE { my ($self) = @_; } sub GETC { my ($self) = @_; } sub PRINT { my ($self, @list) = @_; } sub PRINTF { my ($self, $format, @list) = @_; } sub OPEN { my ($self, $filename) = @_; } sub CLOSE { my ($self) = @_; } sub TELL { my ($self) = @_; } sub EOF { my ($self) = @_; } sub BINMODE { my ($self) = @_; } sub SEEK { my ($self, $offset, $whence) = @_; } sub DESTROY { my ($self) = @_; } #---------------------------------------------------------- package main; tie *HANDLE, 'TieHandleExample'; # TIEHANDLE open HANDLE, "/test/path"; # OPEN syswrite HANDLE, my $addr, my $length, my $offset; # WRITE sysread HANDLE, my $addr, my $length, my $offset; # READ getc HANDLE; # GETC seek HANDLE, my $offset, my $whence; # SEEK tell HANDLE; # TELL eof HANDLE; # EOF binmode HANDLE; # BINMODE read HANDLE, my $addr, my $length, my $offset; # READ my $line = <HANDLE>; # READLINE print HANDLE "line"; # PRINT printf HANDLE "format", "line"; # PRINTF close HANDLE; # CLOSE untie *HANDLE; # DESTROY </source> Для этого типа связывания мы приведем два конкретных примера использования. Наверное вы знаете Unix утилиту [[w:tee|tee]]. Основной задачей этой утилиты является копирование и перенаправление основного потока. Например, этим пользуются, когда вывод программы нужно видеть на консоли и одновременно с этим логировать его в файл (возможно в несколько файлов). В Perl можно реализовать возможности <code>tee</code> следующим образом. <source lang="perl"> #!/usr/bin/perl use strict; package Tie::Tee; sub TIEHANDLE { my $class = shift; my @handles; # В массиве мы сохраняем файловые дескрипторы, # в которые будет вестись параллельный вывод. for my $path (@_) { # Открываем дескрипторы по передаваемым параметрам. open(my $fh, ">$path") || die "can't write $path"; push @handles, $fh; } bless \@handles, $class; } sub PRINT { my $self = shift; my $ok = 0; for my $fh (@$self) { # Пишем в каждый открытый дескриптор. $ok += print $fh @_; } return $ok == @$self; } sub DESTROY { my $self = shift; for my $fh (@$self) { close $fh; } } #---------------------------------------------------------- package main; tie *BROADCAST, "Tie::Tee", qw(tmp1 - tmp2 >tmp3 tmp4); for my $line (qw {Apple Pear Grapes}) { print BROADCAST "$line\n"; } untie *BROADCAST; </source> В примере выше мы открываем 5 дескрипторов: 4 связывается с файлами (<code>tmp1</code>, <code>tmp2</code>, <code>tmp3</code>, <code>tmp4</code>), причем <code>tmp3</code> открывается на дозапись, и один связывается со стандартным потоком вывода (<code>-</code>). Все эти дескрипторы представляет один общий, который мы назвали <code>BROADCAST</code> и к которому мы привязали свою реализацию метода <code>PRINT</code>. Она не очень сложная и занимается тем, что для каждого открытого ранее дескриптора вызывает функцию <code>print</code> и передает список для печати. После запуска программы, вы можете видеть, как создаются 4 файла, и один и тот же вывод транслируется в каждый из них и на консоль. Следующий пример показывает, как приложение может переключать источник ввода. Если приложению не передано ни одного аргумента командной оболочки, то приложение читает стандартный поток ввода. <source lang="perl"> use strict; package StdinSelector; sub TIEHANDLE { my $class = shift; my @lines = @_; bless \@lines, $class; } sub READLINE { my ($self) = @_; shift @$self; } #---------------------------------------------------------- package main; tie *STDIN, 'StdinSelector', @ARGV if scalar @ARGV != 0; # В этом примере важно писать имя дескриптора явно. while(<STDIN>) { chomp; print $_, "\n"; } untie *STDIN if tied *STDIN; </source> <source lang="bash"> # Аргументы переданы явно $ perl ./selecthandle.pl 1 2 3 1 2 3 # А теперь данные поставляются через STDIN. $ seq 1 3 | perl ./selecthandle.pl 1 2 3 </source> === Перегрузка операций через модуль overload === Второй подход перегрузки операций заключается в использовании директивного модуля <code>overload</code>. Данный модуль позволяет перегрузить часть стандартных операций в отношении произвольного класса. Данный модуль разрешает перегружать только те операции, которые перечислены в хеш-массиве <code>%overload::ops</code>. На момент версии 5.40 состав перегружаемых операций таков: <source lang="bash"> $ perl -e 'use overload; use Data::Dumper; print Dumper \%overload::ops;' $VAR1 = { '3way_comparison' => '<=> cmp', 'iterators' => '<>', 'with_assign' => '+ - * / % ** << >> x .', 'mutators' => '++ --', 'filetest' => '-X', 'dereferencing' => '${} @{} %{} &{} *{}', 'conversion' => 'bool "" 0+ qr', 'matching' => '~~', 'unary' => 'neg ! ~ ~.', 'binary' => '& &= | |= ^ ^= &. &.= |. |.= ^. ^.=', 'assign' => '+= -= *= /= %= **= <<= >>= x= .=', 'num_comparison' => '< <= > >= == !=', 'func' => 'atan2 cos sin exp abs log sqrt int', 'special' => 'nomethod fallback =', 'str_comparison' => 'lt le gt ge eq ne' }; </source> Обратите внимание, что операции в этом массиве отсортированы по категориям. Большинство операций в этом списке однозначно соответствуют их знакам, но есть и исключения. * Операции <code>not</code> нет в списке перегружаемых операций, однако, если вы перегружаете <code>!</code>, то при вызове <code>not</code> будет использоваться реализация для <code>!</code>. * Точно так же операция унарного минуса <code>-</code> будет использовать реализацию для <code>neg</code>. * При перегрузке инкремента <code>++</code> и декремента <code>--</code>, Perl не будет делать отдельные реализации для префиксной и постфиксной формы (как, например, это делается в языке C++), однако точка вызова их в выражении будет поставлена так, чтобы отрабатывала префиксная или постфиксная форма. Эти операции находятся в категории <code>mutators</code>, что говорит о том, что они работают не с копией значения, а ссылкой на него. * Операцию присваивания <code>=</code> нельзя перегрузить с помощью этого модуля, так как она зарезервирована для конструктора копирования класса. Единственный способ её перегрузить — это использовать интерфейс <code>tie()</code>. Тем не менее, вы можете перегружать прочие разновидности присваивания (категория <code>assign</code>), так как они работают с ссылкой. * Операции<pre>+ - * / % ** << >> x . & | ^ &. |. ^.</pre>не должны изменять свои операнды, так как они передаются по ссылке и это было бы некорректным поведением. Другими словами, они должны конструировать новое значение в качестве результата. * Все операции тестирования файла (<code>-f</code>, <code>-x</code> и др.) перегружаются одной и той же операцией <code>-X</code>, и перегрузить их по-отдельности невозможно. Тем не менее, вы можете распознать какой тест передаётся через второй аргумент, несмотря на то, что операция унарная. * Чтобы отличать побитовые операции от прочих, в таблице <code>%overload::ops</code> используется точка, например, <code>&</code> и <code>&.</code>. Обычно, если вам требуется перегрузить операции для вашего класса, минимально рекомендуется перегружать следующие: <pre> + - * / % ** << >> x <=> cmp & | ^ ~ &. |. ^. ~. atan2 cos sin exp log sqrt int "" 0+ bool ~~ </pre> ==== Основы перегрузки ==== * Перегрузка операции происходит в момент вызова модуля <code>overload</code>. Вы должны указать знак операции из таблицы <code>%overload::ops</code> в качестве ключа, а затем присвоить ссылку на процедуру любым доступным способом<source lang="perl"> use overload "-" => "minus", # Перегрузка с помощью символической ссылки. "*=" => \&muas, # Перегрузка с помощью жесткой ссылки. '""' => sub { ...; }; # Объявление анонимной процедуры на месте. </source> * Объявленные процедуры должны быть реализованы в ближайшем к директиве пакете. Обычно это пакет класса, которому требуется перегрузка операций. Допустимо указывать реализацию из другого пакета. Кроме того, если реализация наследуется, то к ней справедливы все правила наследования, описанные выше. Например, если есть классы <code>A -> B -> C</code>, где стрелка указывает на родителя, и классы <code>B</code> и <code>С</code> перегружают одну и ту же операцию, <code>A</code> будет искать по списку <code>@ISA</code> ближайшую в цепочке наследования реализацию. * Все перегружающие процедуры объявляются одинаково<source lang="perl"> sub overloadImpl { my ($self, $other, $swap) = @_; ... return $result; } </source>Однако, интерпретация входящих аргументов и тип возвращаемого значения зависят от смысла операции (см. ниже). * Для некоторых операций, которые связаны по смыслу разными знаками, Perl может генерировать реализацию автоматически для той части, которая не была перегружена явно. Например, операции <code>0+</code><ref>Операция <code>0+</code> называется «Венерой» из-за внешней схожести с [[w:Зеркало Венеры|зеркалом Венеры]]. Данная операция пытается преобразовать строку в число в скалярном контексте.</ref>, <code>""</code>, и <code>bool</code> могут предоставить свою реализацию операции <code>!</code>, если та не была перегружена, а хотя бы одна из них была перегружена. * Ключ <code>nomethod</code> может использоваться для объявления универсальной перегрузки, которая будет вызываться для любой не перегруженной индивидуально операции. * Установка флага <code>fallback</code> даёт модулю подсказку, как он должен поступать с вызовом, если не удаётся найти для операции перегруженную реализацию. ==== Реализующая процедура ==== Перегрузка бинарных и унарных операций происходит по одному прототипу входящих параметров<source lang="perl">my ($self, $other, $swap, $nomethod, $binary) = @_;</source>Здесь * <code>$self</code> — ссылка на объект класса, для которого была перегружена операция; * <code>$other</code> — второй операнд или параметр унарной операции; * <code>$swap</code> — флаг, который говорит о том, находится ли ссылка на объект слева (ЛОЖЬ) от знака операции или справа (ИСТИНА). Реализующая процедура обычно возвращает ссылку на результат. Здесь многое зависит от того, какой смысл закладывается в перегрузку. Флаг <code>$swap</code> многогранный. Он используется и в бинарных, и унарных операциях, но ЛОЖЬ в них представляется по-разному. В бинарных операциях ЛОЖЬ представляется пустой строкой <code><nowiki>''</nowiki></code>, а в унарных операциях и операциях класса <code>assign</code> используется <code>undef</code>. Существуют также 4-й и 5-й параметры, которые инициализируются в особых ситуациях. Если перегружена специальная операция <code>nomethod</code>, которая по смыслу похожа на <code>AUTOLOAD</code> (но для операций), 4-й параметр всегда инициализируется знаком операции. Для побитовых операций (<code>&</code>, <code>|</code>, <code>^</code> и <code>~</code>) инициализируется 5-й параметр, чтобы, возможно, подсказать реализации о необходимости работать с операндами как с числами. ==== Автогенерируемые операции ==== Если вызывается операция для некоторого класса и при этом для неё нет перегруженной реализации, модуль <code>overload</code> руководствуется встроенным в него флагом <code>fallback</code>, который по умолчанию имеет значение ИСТИНА. Алгоритм поиска реализации операции таков: # Если операция перегружена в объекте вызываемого класса, то использовать реализацию в этом классе, иначе идём дальше. # Если <code>fallback</code> ИСТИНА или <code>undef</code>, попытаться сгенерировать автоматическую реализацию. # Если операция не относится к категории <code>assign</code>, то повторить шаг 1 для второго операнда. # Повторить шаг 2 для второго операнда. # Если для первого операнда все сводится к <code>nomethod</code> реализации, то использовать её. # Если для второго операнда все сводится к <code>nomethod</code> реализации, то использовать её. # Если <code>fallback</code> ИСТИНА и не нашлось ни одной реализации, то попытаться обработать операнды в соответствии с типом операции. Например, если операция арифметическая, то попытаться привести операнды к числам; если строковая — строкам и т.д. # Если ничего из этого не может корректно отработать, то бросить исключение. В этом алгоритме для унарных операции опускаются шаги, связанные со вторым операндом. Хотя правила выше кажутся прозрачными, существует очень много подводных камней в ситуациях, когда не удаётся найти реализацию, поэтому стоит перегружать операции всегда с большой внимательностью. Для некоторых операций могут использоваться близкие по смыслу операции, которые могут предоставить свою реализацию, если целевая не была определена. Вы можете ознакомиться с ними в официальной документации [https://perldoc.perl.org/overload#Magic-Autogeneration Magic-Autogeneration]. ==== Пример перегрузки ==== На практике перегрузка зачастую используется, чтобы выразить идею программы более наглядно — добавить синтаксического сахара. Пример ниже демонстрирует модуль, который, используя операцию <code><<</code>, представляет поток строк, который, возможно, используется шаблонизатором<ref>Программой, которая, используя некоторый шаблон, генерирует конечный текстовый документ.</ref>. В данный поток можно встраивать условия, чтобы игнорировать некоторые части шаблона в зависимости от текущих условий. Внимательно изучите следующий код. <source lang="perl"> use strict; package StringStream; use subs qw { If ElseIf Else Endl }; use overload '<<' => \&__stream_element, q{""} => \&__to_string; require Exporter; our @ISA = qw{ Exporter }; our @EXPORT = qw{ new If ElseIf Else Endl }; our @EXPORT_OK = qw{ If ElseIf Else Endl }; sub new { my $class = shift; bless { 'if_stack' => [], 'stream' => [] }, $class } sub __to_string { my $self = shift; my $result = ""; foreach my $e (@{$self->{stream}}) { $result .= $e; } return $result; } sub __stream_element { my ($self, $rhs, $swap) = @_; my $stackSize = scalar @{$self->{'if_stack'}}; my $lastCondition = 1; $lastCondition = $self->{'if_stack'}->[-1] if $stackSize; if (ref $rhs eq '_If') { if ($lastCondition) { my $condition = $$rhs; push @{$self->{'if_stack'}}, 's'; push @{$self->{'if_stack'}}, $condition; } else { push @{$self->{'if_stack'}}, 0; } } elsif (ref $rhs eq '_ElseIf') { my $condition = $$rhs; if ( ! $lastCondition && $condition ) { push @{$self->{'if_stack'}}, $condition; } elsif ($lastCondition) { push @{$self->{'if_stack'}}, -1; } } elsif (ref $rhs eq '_Else') { if ( ! $lastCondition && $lastCondition != -1 ) { push @{$self->{'if_stack'}}, 1; } else { push @{$self->{'if_stack'}}, 0; } } elsif (ref $rhs eq '_Endl') { return $self if ! $stackSize; my $element = undef; do { $element = pop @{$self->{'if_stack'}} } while ($element ne 's'); } else { if ($lastCondition && $lastCondition != -1) { push @{$self->{'stream'}}, $rhs; } } return $self; } package _If; use overload '<<' => \&StringStream::__stream_element ; sub If($;) { my ($condition) = @_; return bless \$condition, '_If'; } package _ElseIf; use overload '<<' => \&StringStream::__stream_element ; sub ElseIf($;) { my ($condition) = @_; return bless \$condition, '_ElseIf'; } package _Else; use overload '<<' => \&StringStream::__stream_element ; sub Else () { return bless {}, '_Else'; } package _Endl; use overload '<<' => \&StringStream::__stream_element ; sub Endl () { return bless {}, '_Endl'; } package StringStream; sub If($;) { local $_ = $@; &_If::If; } sub ElseIf($;) { local $_ = $@; &_ElseIf::ElseIf; } sub Else() { local $_ = $@; &_Else::Else; } sub Endl() { local $_ = $@; &_Endl::Endl; } 1; </source> Данный модуль представляет класс <code>StringStream</code>, который имеет два атрибута: <code>stream</code> и <code>if_stack</code>. Атрибут <code>stream</code> представляет собой ссылку на строковый массив, в который набираются части результирующей строки. Чтобы в потоке строк иметь возможность игнорировать некоторые части, класс реализует собственные операции, которые могут представлять условия. Они реализуются за счет встроенных классов, которые не видны клиентскому коду, но которые может интерпретировать <code>StringStream</code>. Эти классы имитируют конструкцию <code>If...ElseIf...Else</code>, но, чтобы отмечать границу конструкции, используется терминирующая операция <code>Endl</code>. Создать экземпляры встроенных классов можно только с помощью функций-оболочек, которые имеют имена <code>If</code>, <code>ElseIf</code>, <code>Else</code> и <code>Endl</code>, причем в <code>If</code> и <code>ElseIf</code> можно передавать выражения, которые выполняют роль условных конструкций. Весь секрет этого класса кроется в том, что он и его скрытые классы перегружают знак операции <code><<</code>, который отсылает к одной единственной процедуре <code>__stream_element</code>. Данная процедура анализирует второй операнд и, используя массив <code>if_stack</code>, отключает части потока строк, в зависимости от результатов условных выражений. Данная система не проверяет некоторые некорректные ситуации и ожидает, что ни один <code>ElseIf</code> не будет встречаться раньше чем <code>If</code>; <code>Else</code> не будет стоять раньше <code>If</code> и <code>ElseIf</code>, а каждая открытое условие <code>If</code> будет терминироваться <code>Endl</code>. Кроме операции <code><<</code> класс <code>StringStream</code> также перегружает операцию <code>q{""}</code>, которая используется в скалярном контексте, чтобы сформировать из набранной строки окончательную. Ниже показан пример использования. <source lang="perl"> #!/usr/bin/perl # File: stream_test.pl use StringStream; print StringStream->new << "Stream of strings:" << "\n" << If (scalar @ARGV == 0) << " No Arguments passed\n" << ElseIf (scalar @ARGV == 1) << " Passed 1 argument: " << $ARGV[0] << "\n" << Else << " Passed " << (scalar @ARGV) << " arguments" << "\n" << Endl ; </source> В данном примере анализируется входящий массив аргументов и, в зависимости от переданного числа аргументов, печать выводится по-разному: <source lang="perl"> # Предполагается, что файл модуля лежит в рабочем каталоге. $ perl -I$(pwd) ./stream_test.pl Stream of strings: No Arguments passed $ perl -I$(pwd) ./stream_test.pl arg1 Stream of strings: Passed 1 argument: arg1 $ perl -I$(pwd) ./stream_test.pl arg1 arg2 arg3 Stream of strings: Passed 3 arguments </source> Обратите внимание, что мы могли бы конструировать строку без использования этого «сахара», но конечная конструкция изобиловала бы множеством операций конкатенации, ветвлений и циклов. Другими словами, мы концентрируемся на задаче сформировать строку по нашим правилам простыми средствами. == Объектно-ориентированный подход в системе ввода-вывода == С появлением в Perl 5 модулей, в систему ввода-вывода также был добавлен ООП подход в виде интерфейсного класса [https://perldoc.perl.org/IO::Handle IO::Handle] и его классов-потомков, таких как <code>IO::File</code>, <code>IO::Pipe</code>, <code>IO::Socket</code> и других. Это позволяет облегчить написание программ, требующих работы с операциями [[../Ввод и вывод|ввода и вывода]], рассмотренные нами ранее. В данном разделе мы рассмотрим этот интерфейс, чтобы продемонстрировать, как ООП подход может упростить и унифицировать написание кода, связанного с вводом-выводом по файловым дескрипторам. === Интерфейс IO::Handle === Интерфейс, реализуемый модулем <code>IO::Handle</code>, содержит универсальные методы, применяемые для всех объектов, которые в своей реализации открывают файловый дескриптор. Более специализированные классы, например <code>IO::File</code> (обычный файл), предоставляют дополнительные методы, учитывающие нюансы работы с данным типом файла, но так или иначе переопределяют методы, вводимые классом <code>IO::Handle</code>. Вместе эти классы сглаживают некоторые недостатки встроенных дескрипторов файлов Perl и упрощают понимание и сопровождение программ. Объект <code>IO::Handle</code> может быть построен одним из двух конструкторов: * '''Конструктор без аргументов''' (<code>new()</code>) позволяет построить пустой объект класса <code>IO::Handle</code>. Далее необходимо сделать библиотечный вызов <code>fdopen()</code> через одноименный метод, чтобы открыть файловый дескриптор с определенным режимом. Так как этот вызов требует знания флагов и атрибутов, его вызов не очень удобен на практике. Конструкторы в классах, наследующих этот интерфейс, скрывают этот вызов в своих реализациях. * '''Конструктор с аргументами''' (<code>new_from_fd ()</code>) позволяет построить объект и привязать к нему уже открытый дескриптор. Главным образом используется, когда нет нужной реализации интерфейса, но требуется использовать методы <code>IO::Handle</code>. Методы <code>IO::Handle</code> в основном копируют одноименные функции ввода-вывода: <source lang="perl"> $io->close # Закрыть дескриптор. $io->eof # Возвращает признак конца потока EOF. $io->fcntl( FUNCTION, SCALAR ) # Управляет флагами файлового дескриптора. $io->fileno # Возвращает номер файлового дескриптора. $io->format_write( [FORMAT_NAME] ) # Аналог функции write(), т.е. запись по формату. $io->getc # Прочитать очередной символ. $io->ioctl( FUNCTION, SCALAR ) # Делает системный вызов ioctl() для управления устройством ввода-вывода. $io->read ( BUF, LEN, [OFFSET] ) # Буферизированное чтение. $io->write ( BUF, LEN [, OFFSET ] ) # Буферизированная запись. $io->print ( ARGS ) # Буферизированная запись. $io->printf ( FMT, [ARGS] ) # Буферизированная запись по форматной строке. $io->say ( ARGS ) # Буферизированная запись с переносом строки. $io->stat # Реализация функции stat(). $io->sysread ( BUF, LEN, [OFFSET] ) # Не буферизированное чтение $io->syswrite ( BUF, [LEN, [OFFSET]] ) # Не буферизированная запись. $io->truncate ( LEN ) # Смещает указатель записи в начало, что аналогично удалению данных. $io->opened # Возвращает ИСТИНУ, если дескриптор открыт. $io->getline # Возвращает строку до разделителя. $io->getlines # Возвращает строки до конца потока. $io->ungetc ( ORD ) # Возвращает из потока символ с указанным порядковым номером. $io->error # Возвращает ИСТИНУ, если возникала ошибка ввода-вывода. $io->clearerr # Сбрасывает флаг ошибки. Возвращает -1, если дескриптор находится в состоянии ошибки. $io->sync # Реализует синхронизацию данных в буфере и на носителе на уровне системы (не PerlIO). Может не поддерживаться на некоторых системах. $io->flush # Сброс буферов ввода-вывода, связанных с дескриптором. $io->printflush ( ARGS ) # Реализует сброс буферов только для текущей операции, а затем восстанавливает состояние флага сброса. $io->blocking ( [ BOOL ] ) # Если в аргументе передается ИСТИНА, делает операции ввода-вывода неблокирующими. </source> Также интерфейс позволяет управлять встроенными переменными языка Perl, управляющими вводом-выводом. Следующие методы управляют значениями для конкретного экземляра класса: <source lang="perl"> $io->autoflush ( [BOOL] ) # Управляет $| (управление буферизацией) $io->format_page_number( [NUM] ) # Управляет $% (номер страницы при выводе по формату) $io->format_lines_per_page( [NUM] ) # Управляет $= (число строк на страницу при выводе по формату) $io->format_lines_left( [NUM] ) # Управляет $- (число строк на текущей странице при выводе по формату) $io->format_name( [STR] ) # Управляет $~ (имя текущего формата) $io->format_top_name( [STR] ) # Управляет $^ (имя формата, который является верхним колонтитулом) $io->input_line_number( [NUM]) # Управляет $. (номер последней прочитанной строки последнего удачного чтения) </source> Следующие переменные управляются из этого интерфейса, но применяются ко всем открытым дескрипторам (т.е. методы являются статическими): <source lang="perl"> IO::Handle->format_line_break_characters( [STR] ) # $: (хранит символы переноса строк) IO::Handle->format_formfeed( [STR]) # $^L (управляющий символ перехода на новую страницу при выводе по формату) IO::Handle->output_field_separator( [STR] ) # $, (разделитель полей при печати функцией print) IO::Handle->output_record_separator( [STR] ) # $\ (символ, добавляемый в конец при печати функцией print) IO::Handle->input_record_separator( [STR] ) # $/ (символ разделителя записей при чтении из файлов) </source> Ниже показан небольшой пример работы с <code>IO::Handle</code>. <source lang="perl"> # Файл: handle.pl use strict; use warnings; use IO::Handle; my @strings; # Открываем дубликат дескриптора STDIN, чтобы работать с ним из экземпляра IO::Handle. my $io = IO::Handle->new_from_fd(fileno(STDIN),"r"); if ($io->opened) { push @strings, $_ while $_ = $io->getline; $io->close; print "Record number: " . @strings . "\n"; } # Следующая печать показывает, что закрылся только дубликат, а не STDIN. print "Object \$io " . ($io->opened ? "is opened" : "is closed") . "\n"; # При подключении модуля IO::Handle, все открытые дескрипторы неявно получают доступ к интерфейсным методам. print "STDIN " . (STDIN->opened ? "is opened" : "is closed") . "\n"; our $out; # Открываем дубликат дескриптора STDOUT, чтобы работать с ним из экземпляра IO::Handle. # Для примера, дубликат открывается методом fdopen(). { $out = IO::Handle->new(); if ($out->fdopen(fileno(STDOUT),"w")) { print "Printing 1:\n"; foreach my $line (@strings) { $out->write($line); } } # При обнулении ссылки, будет вызван деструктор, в котором close() вызывается неявно. undef $out; } # Мы могли не создавать дубликат STDOUT, а писать в него напрямую через интерфейс. print "Printing 2:\n"; foreach my $line (@strings) { STDOUT->write($line); } </source> <source lang="bash"> $ echo -e "aaa\nbbb\nccc" | perl handle.pl Record number: 3 Object $io is closed STDIN is opened Printing 1: aaa bbb ccc Printing 2: aaa bbb ccc </source> Приведенный выше пример плохо показывает, чем ООП парадигма в данном случае лучше процедурной, однако, мы всё же перечислим некоторые преимущества такого подхода: * Вы можете заметить, что вы не работаете с дескрипторами напрямую. По сути вы работаете с ссылками на объекты, которые инкапсулируют дескриптор внутри себя. Если бы программа была больше и в ней бы требовалось передавать дескриптор различным её частям, поддерживать код с передачей ссылки на объект намного проще, чем передачу дескриптора. * Жизненный цикл объекта отчасти перекладывается на систему очистки ссылок Perl: вам легче освобождать ресурсы системы, так как их освобождают автоматически вызываемые деструкторы. * В начале исходного файла вы всегда подключаете нужный вам модуль пространства <code>IO::</code>, что может давать подсказку читающему программу, что данный код работает с определенным типом файла. При работе с дескрипторами напрямую, невозможно дать такую подсказку без длинного комментария. В большой программе (в несколько тысяч строк), чтобы уловить намерения работы с дескриптором, вам придется внимательно изучить все вызовы, в которых участвует дескриптор. * При любых фатальных ошибках конкретная реализация интерфейса, как правило, выбрасывает исключение, которое перехватывать проще, чем проверять коды возврата. === Примеры === Ниже приведено несколько примеров применения потомков <code>IO::Handle</code>. Следующая программа открывает простой файл, переданный аргументом, и считает сколько в нём строк. В этом примере используется класс <code>IO::File</code> для работы с файлом в ООП стиле. <source lang="perl"> # Файл: line_counter.pl use strict; use warnings; use IO::File; my $file = shift; my $counter = 0; my $io = IO::File->new($file) or die "Cannot open $file: $!\n"; $counter++ while defined $io->getline; STDOUT->print("File '$file' contains $counter lines\n"); </source> <source lang="bash"> $ cat > file.txt <<EOF 1 2 3 4 5 6 EOF $ perl line_counter.pl file.txt File 'file.txt' contains 6 lines </source> Вернемся к примеру, рассмотренному в разделе [[../Ввод и вывод#Открытие канала через pipe()|Открытие канала через pipe()]]. При работе с каналом в процедурном стиле код получается довольно громоздким. Теперь посмотрите на то, как код будет выглядеть, если управлять каналом через объект класса <code>IO::Pipe</code>. <source lang="perl"> # Файл: pipe.pl use strict; use warnings; use IO::Pipe; # Строим объект одностороннего канала. my $pipe = IO::Pipe->new(); # Создаем со-процесс. my $pid = fork(); if (defined $pid) { if ($pid == 0) { # Пересоздаем объект так, чтобы на этом конце можно было только писать в канал. # Также отключаем буферизацию. $pipe->writer()->autoflush(); $pipe->print("Hello from child process\n"); $pipe->close(); exit 0; } else { # Пересоздаем объект так, чтобы на этом конце можно было только читать канал. $pipe->reader(); while (defined (my $line = $pipe->getline)) { STDOUT->print("Parent received: $line"); } $pipe->close(); waitpid $pid, 0; } } else { die "Could not fork: $!"; } </source> <source lang="bash"> $ perl pipe.pl Parent received: Hello from child process </source> Обратите внимание, что код стал намного чище. Наконец, продемонстрируем простейший Web-клиент, использующий модуль <code>IO::Socket</code>. Использование ООП подхода в этой задаче избавляет вас от изнурительного труда по настройке сетевого сокета через библиотеку <code>Socket</code>. <source lang="perl"> # Файл: pcurl.pl use strict; use warnings; # Импортируем IO::Socket и подключаем некоторые константы. use IO::Socket qw ( :DEFAULT :crlf ); # Изменяем стандартный разделитель строк на разделитель CRLFCRLF, так как им # заканчивается список заголовков. IO::Handle->input_record_separator( CRLF . CRLF ); # Пользователь передает URL для подключения. my $url = shift or die "pcurl.pl <URL>\n"; my $data; # Регулярными выражениями выделяем в URL хост и путь до ресурса. Этот клиент работает # только по протоколу HTTP. my ($host, $path) = $url =~ m!^http://([^/]+)(/[^\#]*)! or die "Invalid URL.\n"; # Открываем сетевой сокет и пытаемся подключиться к сетевой службе по порту 80. my $socket = IO::Socket::INET->new(PeerAddr => $host, PeerPort => 'http(80)') or die "Cannot connect: $!"; # Посылаем запрос на сервер по переданному пути. $socket->print("GET $path HTTP/1.0", CRLF, CRLF); # Отделяем заголовки от полезной нагрузки. my $header = <$socket>; $header =~ s/$CRLF/\n/g; # Печатаем заголовки. STDOUT->print($header); # Печатаем сообщение ответа. STDOUT->print($data) while ($socket->read($data, 1024) > 0); </source> Для примера, обратимся к ресурсу [http://httpbin.org/ip httpbin.org/ip], который является эхо-сервером, возвращающим ваш IP адрес, по которому вы к нему подключаетесь. <source lang="perl"> $ perl pcurl.pl 'http://httpbin.org/ip' HTTP/1.1 200 OK Date: Sat, 27 Sep 2025 08:43:20 GMT Content-Type: application/json Content-Length: 29 Connection: close Server: gunicorn/19.9.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true { "origin": "5.206.96.5" } </source> Надеемся, что эти простейшие примеры дадут вам представления о некоторых преимуществах ООП подхода. == Примечания == {{примечания}} 8xfj9m1wjqejuipssicpb6b2wn8txa1 АОН/ДО 0 32578 269198 257526 2026-06-24T17:08:42Z Leksey 3027 269198 wikitext text/x-wiki '''Диспетчерское обслуживание''' (сокращенно ДО) один из трех видов [[АОН/ОВД|обслуживания воздушного движения]] (ОВД) и один из основных. Обеспечиваются им те ВС, что выполняют полеты в классах А и С. При полете в классе Гольф такого вида обслуживания ВС не получают. Осуществляется только в пределах контролируемого ВП (класс А, С и контролируемые аэродромы) и основывается на выдаче указаний и разрешений диспетчером экипажам. ==Виды ДО== *Районное диспетчерское обслуживание Обычно это обслуживание горизонтального полета по маршрутам ОВД в верхнем и нижнем воздушном пространстве *Диспетчерское обслуживание подхода Управление вылетом и прилетом воздушных судов, первоначальный набор и снижение, формирование потоков на вылет и посадку * Аэродромное диспетчерское обслуживание Это управление движением воздушных судов на аэродроме ==Диспетчерским обслуживанием обеспечиваются== * все полеты по правилам полетов по приборам ([[АОН/ППП|ППП]]) в воздушном пространстве классов A и C; * все полеты по правилам визуальных полетов ([[АОН/ПВП|ПВП]]) в воздушном пространстве класса C; * все виды аэродромного движения на контролируемых аэродромах. ==Органы ДО== {{Основная статья|АОН/ДО/Органы}} ==См. также== *[[АОН/ОВД|ОВД]] {{АОН}} pxpsn9g3xqnwxpe25d4baa8hrr7rroy АОН/ДО/Органы 0 32579 269194 260827 2026-06-24T16:54:31Z Leksey 3027 Уточнения 269194 wikitext text/x-wiki Обслуживание воздушного движения ([[АОН/ОВД|ОВД]]) осуществляют органы диспетчерского обслуживания ([[АОН/ДО|ДО]]). ОВД происходит внутри [[АОН/Воздушное пространство/Зоны и районы|зон и районов]]. К таковым относятся: * Районное диспетчерское обслуживание (РДО) (в АИП переведен как CTA (хотя это также "Диспетчерский район (ДР)")): **Районный диспетчерский центр ([[АОН/РДЦ|РДЦ]]) - (англ. ACC) [[w:Районный диспетчерский центр]]. **Районный центр ([[АОН/РЦ|РЦ]]) **Местный диспетчерский пункт ([[АОН/МДП|МДП]]) (как LCU перевели в AIP) //МДА это для Advisory/Information Services? **Диспетчерский пункт подхода ([[АОН/ДПП|ДПП]]) (в случае, если не созданы РДЦ, РЦ, МДП) * Диспетчерское обслуживание подхода (ДОП): **[[АОН/ДПП|ДПП]](в случае создания отдельного органа) **[[АОН/РДЦ|РДЦ]], [[АОН/РЦ|РЦ]], [[АОН/МДП|МДП]](в случае объединения функций РДО и ДОП) *Аэродромное диспетчерское обслуживание **Диспетчерский пункт аэродрома (один или несколько) ==Источник== * AIP GEN 2.2 Сокращения, используемые в аэронавигационной информационной продукции ==См. также== *[[АОН/Воздушное_пространство/Структура]] *[[АОН/ДО|Диспетчерское обслуживание]] *[[АОН/Воздушное пространство/Зоны и районы]] {{АОН}}{{BookCat}} bd5677a810f2hltehiv3fe3g4revsf8 АОН/Диспетчер 0 32789 269193 269137 2026-06-24T16:52:18Z Leksey 3027 /* Диспетчер ОВД */ стиль 269193 wikitext text/x-wiki {{АОН Страница}} {{Википедия|Авиадиспетчер}} '''{{SUBPAGENAME}}''' — в русском языке применительно к гражданской авиации это обозначение нескольких похожих профессий. Диспетчер, что непосредственно занимается общением с экипажами воздушных судов и сидит перед экраном радара, на котором отображается положением воздушных судов называется диспетчером [[АОН/ОВД|ОВД]]. В каких-то нормативных документов он может по старинке именоваться "диспетчером управления воздушным движением" (слово "управления" уже убирается и заменяется на "обслуживание"). В большинстве случае это государственный служащий - сотрудник [[АОН/Госкорпорация|Госкорпорации]]. Второй тип диспетчеров - это [[АОН/Полетный_диспетчер|полетный диспетчер]], который является сотрудником той или иной авиакомпании. ==Диспетчер ОВД== Английское обозначение диспетчера обслуживания воздушного движения такого специалиста - "Air traffic controller" (сокращенно ATC) и слово диспетчер в обозначении его профессии отсутствует. А диспетчерами за рубежом называют сотрудников, осуществляющих подготовку к рейсам и их планирование. Которые являются сотрудниками авиакомпаний и не сидят перед пультом. Русское название их [[АОН/Полетный диспетчер|полетный диспетчер]]. Какие могут быть диспетчера в системе ОВД *Диспетчер службы движения, занятый планированием воздушного движения *Диспетчер аэродромного диспетчерского пункта *Диспетчер руления *Диспетчер Вышки *диспетчер круга. *Диспетчер подхода *Диспетчер РЦ (районного центра) (верхнее воздушное пространство) *Диспетчер диспетчерскому пункту (МДП) и диспетчер полетно-информационного обслуживания (ПИО)(нижнее воздушное пространство) ==ВЛЭК для диспетчера и свидетельство== Для диспетчеров также требуется прохождение ВЛЭК и получение медицинского заключения. ==См. также== * [[АОН/ОВД|ОВД]] * [[АОН/Полетный_диспетчер|Полетный диспетчер]] - диспетчер планирующий полеты. Английское название [[w:en:Flight dispatcher|Flight dispatcher]]. == Ссылки == ==Примечания== {{Примечания}} {{АОН}} ouxx5wmfs0ihguvjhomwecec682zrvp 269197 269193 2026-06-24T17:04:07Z Leksey 3027 Викификация 269197 wikitext text/x-wiki {{АОН Страница}} {{Википедия|Авиадиспетчер}} '''{{SUBPAGENAME}}''' — в русском языке применительно к гражданской авиации это обозначение нескольких похожих профессий. Диспетчер, что непосредственно занимается общением с экипажами воздушных судов и сидит перед экраном радара, на котором отображается положением воздушных судов называется [[АОН/Диспетчер ОВД|диспетчером ОВД]]. В каких-то нормативных документов он может по старинке именоваться "диспетчером управления воздушным движением" (слово "управления" уже убирается и заменяется на "обслуживание"). В большинстве случае это государственный служащий - сотрудник [[АОН/Госкорпорация|Госкорпорации]]. Второй тип диспетчеров - это [[АОН/Полетный_диспетчер|полетный диспетчер]], который является сотрудником той или иной авиакомпании. ==Диспетчер ОВД== {{Основная статья|АОН/Диспетчер ОВД}} Английское обозначение диспетчера обслуживания воздушного движения такого специалиста - "Air traffic controller" (сокращенно ATC) и слово диспетчер в обозначении его профессии отсутствует. А диспетчерами за рубежом называют сотрудников, осуществляющих подготовку к рейсам и их планирование. Которые являются сотрудниками авиакомпаний и не сидят перед пультом. Какие могут быть диспетчера в системе ОВД: *Диспетчер службы движения, занятый планированием воздушного движения *Диспетчер аэродромного диспетчерского пункта *Диспетчер руления *Диспетчер Вышки *диспетчер круга *Диспетчер подхода *Диспетчер РЦ (районного центра) (верхнее воздушное пространство) *Диспетчер диспетчерскоко пункта (МДП) и диспетчер полетно-информационного обслуживания (ПИО) (нижнее воздушное пространство) ==Диспетчер планирования== {{Основная статья|АОН/Диспетчер планирования}} == Полетный диспетчер == {{Основная статья|АОН/Полетный диспетчер}} ==ВЛЭК для диспетчера и свидетельство== Для диспетчеров также требуется прохождение ВЛЭК и получение медицинского заключения. ==См. также== * [[АОН/ОВД|ОВД]] * [[АОН/Полетный_диспетчер|Полетный диспетчер]] - диспетчер планирующий полеты. Английское название [[w:en:Flight dispatcher|Flight dispatcher]]. == Ссылки == ==Примечания== {{Примечания}} {{АОН}} oz61kzm4ccr16ya225a49cx3qtfcud7 АОН/Поставщик аэронавигационного обслуживания 0 33167 269203 257621 2026-06-25T03:40:42Z Leksey 3027 Оформление 269203 wikitext text/x-wiki {{АОН Страница}} {{Википедия|w:en:Air navigation service provider}} '''{{SUBPAGENAME}}''' (ПАО) (англ. Air navigation service provider - ANSP) — категория государственных или частных организаций, обеспечивающих в каждом стране-члене ИКАО аэронавигационное обслуживание полетов. Применительно к России такой организацией является [[АОН/Госкорпорация‎|Госкорпорация‎]]. == Задачи АНП == В задачи АО входит: *[[АОН/Госкорпорация|Госкорпорация]] организация воздушного движения (ОрВД) (АТМ) *[[АОН/Госкорпорация|Госкорпорация]] система связи, навигации и наблюдения (CNS) *[[АОН/Авиаметтелеком|Авиаметтелеком]] (MET) *[[АОН/ПСР|ПСР]] (SAR) *[[АОН/ЦАИ|ЦАИ]] (AIS/AIM). ==См. также== * [[АОН/Госкорпорация‎|Госкорпорация]] == Ссылки == * ICAO DOC 9161 ==Примечания== {{Примечания}} {{АОН}} 8ljny951rfics1nyrp7orr3zl1bu1y7 АОН/Авиаметтелеком 0 33168 269204 257622 2026-06-25T03:43:37Z Leksey 3027 См. также 269204 wikitext text/x-wiki {{АОН Страница}} '''{{SUBPAGENAME}}''' (полное название ФГБУ «Авиметтелеком Росгидромета») — дочерняя компания Росгидромета для работы с авиационной погоды и продажи ее потребителям. Носит статус официального [[АОН/Метео/Провайдер|провайдера]] метерологической информации в России. == Публичные сервисы== * [[АОН/Метео/Metavia2|Metavia2.ru]] ==См. также== * [[АОН/Метео/Провайдер|Провайдер]] [[АОН/Метео/Провайдер|метеоинформации]] == Ссылки == *[https://www.aviamettelecom.ru/ Официальный сайт] ==Примечания== {{Примечания}} {{АОН}} kzqt2myk4u3wgi98wzlkj6jirp0owy5 Шаблон:Грамматика Нового Ифкуиля/Таблица Слотов Форматива 10 35768 269200 2026-06-24T20:20:02Z K.gaudium 68081 Создан шаблон 269200 wikitext text/x-wiki <noinclude> {{Documentation}} '''Пример использования:'''<br> {{Структура слотов форматива |hl_II=1 |hl_III=1 |hl_IV=1}} </noinclude><includeonly> <table style="border-collapse:collapse; border:1px solid #000; width:100%; font-size:90%; margin:1em 0;"> <tr> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">I</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">II</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">III</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">IV</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">V</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">VI</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">VII</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">VIII</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">IX</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">X</th> </tr> <tr> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_I|}}}|background-color:#ffffcc;}}">(C<sub>c</sub><br>V<sub>v</sub>)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_II|}}}|background-color:#ffffcc;}}">C<sub>r</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_III|}}}|background-color:#ffffcc;}}">V<sub>r</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_IV|}}}|background-color:#ffffcc;}}">(C<sub>s</sub>V<sub>x</sub>...)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_V|}}}|background-color:#ffffcc;}}">C<sub>a</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VI|}}}|background-color:#ffffcc;}}">(V<sub>x</sub>C<sub>s</sub>...)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VII|}}}|background-color:#ffffcc;}}">(V<sub>n</sub><br>C<sub>n</sub>)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VIII|}}}|background-color:#ffffcc;}}">V<sub>c</sub> / V<sub>k</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_IX|}}}|background-color:#ffffcc;}}">[stress]</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_X|}}}|background-color:#ffffcc;}}">[stress]</td> </tr> <tr> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_I|}}}|background-color:#ffffcc;}}">Показатель состояния конкатенации</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_II|}}}|background-color:#ffffcc;}}">Версия + Основа</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_III|}}}|background-color:#ffffcc;}}">Корень</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_IV|}}}|background-color:#ffffcc;}}">Функция + Спецификация + Контекст</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_V|}}}|background-color:#ffffcc;}}">Аффикс(ы) V<sub>x</sub>C<sub>s</sub> применяется к основе, но не к C<sub>a</sub>. Форма имеет вид -C<sub>s</sub>V<sub>x</sub>- (то есть является зеркальным отражением стандартной формы V<sub>x</sub>C<sub>s</sub> в Слоте VII)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_VI|}}}|background-color:#ffffcc;}}">Конфигурация + Протяжённость + Принадлежность + Перспектива + Сущность</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_VII|}}}|background-color:#ffffcc;}}">Аффиксы V<sub>x</sub>C<sub>s</sub> применяются к основе + C<sub>a</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_VIII|}}}|background-color:#ffffcc;}}">Валентность + Наклонение/Область действия падежа<br>или Вид + Наклонение/Область действия падежа<br>или Фаза + Наклонение/Область действия падежа<br>или Уровень + Наклонение/Область действия падежа<br>или Эффект + Наклонение/Область действия падежа</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_IX|}}}|background-color:#ffffcc;}}">Падеж или Формат или Иллокуция + Валидация (в зависимости от ударения в Слоте X)</td> <!-- Слот X: объединённая ячейка (rowspan=2) --> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_X|}}}|background-color:#ffffcc;}}" rowspan="2"> Ударение на предпоследнем слоге = НЕОБРАМЛЁННОЕ отношение + V<sub>c</sub><br> Ударение на последнем слоге = НЕОБРАМЛЁННОЕ отношение + V<sub>k</sub><br> Ударение на третьем с конца слоге = ОБРАМЛЁННОЕ отношение + V<sub>c</sub> </td> </tr> <tr> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_I|}}}|background-color:#ffffcc;}}">Форма согласного, содержащая либо гортанную смычку, либо форму, начинающуюся с -h-.</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_II|}}}|background-color:#ffffcc;}}">Гласный аффикс</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_III|}}}|background-color:#ffffcc;}}">Форма согласного</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_IV|}}}|background-color:#ffffcc;}}">Гласный аффикс</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_V|}}}|background-color:#ffffcc;}}">Согласная + гласная</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VI|}}}|background-color:#ffffcc;}}">Если заполнен слот V, то C<sub>a</sub> удваивается</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VII|}}}|background-color:#ffffcc;}}">Гласная + согласная</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VIII|}}}|background-color:#ffffcc;}}">Модульный слот, состоящий из формы гласного + формы согласного</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_IX|}}}|background-color:#ffffcc;}}">Гласный аффикс</td> </tr> </table> </includeonly> 5mpawsu5zxtj4yy3l4wmw7ofoi0fs0h 269201 269200 2026-06-24T20:21:09Z K.gaudium 68081 Исправлен пример использования 269201 wikitext text/x-wiki <noinclude> {{Documentation}} '''Пример использования:'''<br> {{Шаблон:Грамматика Нового Ифкуиля/Таблица Слотов Форматива |hl_II=1 |hl_III=1 |hl_IV=1}} </noinclude><includeonly> <table style="border-collapse:collapse; border:1px solid #000; width:100%; font-size:90%; margin:1em 0;"> <tr> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">I</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">II</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">III</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">IV</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">V</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">VI</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">VII</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">VIII</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">IX</th> <th style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; background-color:#f0f0f0; font-weight:bold;">X</th> </tr> <tr> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_I|}}}|background-color:#ffffcc;}}">(C<sub>c</sub><br>V<sub>v</sub>)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_II|}}}|background-color:#ffffcc;}}">C<sub>r</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_III|}}}|background-color:#ffffcc;}}">V<sub>r</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_IV|}}}|background-color:#ffffcc;}}">(C<sub>s</sub>V<sub>x</sub>...)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_V|}}}|background-color:#ffffcc;}}">C<sub>a</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VI|}}}|background-color:#ffffcc;}}">(V<sub>x</sub>C<sub>s</sub>...)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VII|}}}|background-color:#ffffcc;}}">(V<sub>n</sub><br>C<sub>n</sub>)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VIII|}}}|background-color:#ffffcc;}}">V<sub>c</sub> / V<sub>k</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_IX|}}}|background-color:#ffffcc;}}">[stress]</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_X|}}}|background-color:#ffffcc;}}">[stress]</td> </tr> <tr> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_I|}}}|background-color:#ffffcc;}}">Показатель состояния конкатенации</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_II|}}}|background-color:#ffffcc;}}">Версия + Основа</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_III|}}}|background-color:#ffffcc;}}">Корень</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_IV|}}}|background-color:#ffffcc;}}">Функция + Спецификация + Контекст</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_V|}}}|background-color:#ffffcc;}}">Аффикс(ы) V<sub>x</sub>C<sub>s</sub> применяется к основе, но не к C<sub>a</sub>. Форма имеет вид -C<sub>s</sub>V<sub>x</sub>- (то есть является зеркальным отражением стандартной формы V<sub>x</sub>C<sub>s</sub> в Слоте VII)</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_VI|}}}|background-color:#ffffcc;}}">Конфигурация + Протяжённость + Принадлежность + Перспектива + Сущность</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_VII|}}}|background-color:#ffffcc;}}">Аффиксы V<sub>x</sub>C<sub>s</sub> применяются к основе + C<sub>a</sub></td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_VIII|}}}|background-color:#ffffcc;}}">Валентность + Наклонение/Область действия падежа<br>или Вид + Наклонение/Область действия падежа<br>или Фаза + Наклонение/Область действия падежа<br>или Уровень + Наклонение/Область действия падежа<br>или Эффект + Наклонение/Область действия падежа</td> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_IX|}}}|background-color:#ffffcc;}}">Падеж или Формат или Иллокуция + Валидация (в зависимости от ударения в Слоте X)</td> <!-- Слот X: объединённая ячейка (rowspan=2) --> <td style="border:1px solid #000; padding:6px 8px; text-align:left; vertical-align:top; {{#if:{{{hl_X|}}}|background-color:#ffffcc;}}" rowspan="2"> Ударение на предпоследнем слоге = НЕОБРАМЛЁННОЕ отношение + V<sub>c</sub><br> Ударение на последнем слоге = НЕОБРАМЛЁННОЕ отношение + V<sub>k</sub><br> Ударение на третьем с конца слоге = ОБРАМЛЁННОЕ отношение + V<sub>c</sub> </td> </tr> <tr> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_I|}}}|background-color:#ffffcc;}}">Форма согласного, содержащая либо гортанную смычку, либо форму, начинающуюся с -h-.</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_II|}}}|background-color:#ffffcc;}}">Гласный аффикс</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_III|}}}|background-color:#ffffcc;}}">Форма согласного</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_IV|}}}|background-color:#ffffcc;}}">Гласный аффикс</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_V|}}}|background-color:#ffffcc;}}">Согласная + гласная</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VI|}}}|background-color:#ffffcc;}}">Если заполнен слот V, то C<sub>a</sub> удваивается</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VII|}}}|background-color:#ffffcc;}}">Гласная + согласная</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_VIII|}}}|background-color:#ffffcc;}}">Модульный слот, состоящий из формы гласного + формы согласного</td> <td style="border:1px solid #000; padding:6px 8px; text-align:center; vertical-align:middle; {{#if:{{{hl_IX|}}}|background-color:#ffffcc;}}">Гласный аффикс</td> </tr> </table> </includeonly> 6dljo30lwdw2ous42s9ga1zaadpf44i