Perl6 — Ещё немного о блоках (Phasers)

в 20:44, , рубрики: async, Enter, lazy, perl, perl6, Программирование, метки: , , ,

1. Особенности работы с переменными и литералами в Perl6
2. Perl6 — Операции над переменными, анонимные блоки
3. Perl6 — Условные операторы, циклы
4. Perl6 — Работа с функциями
5. Perl6 — Классы
6. Perl6 — Ввод-вывод, модули
7. Perl6 — Комментарии, пробельные символы, скобки
8. Perl6 — Перегрузка операторов
9. Perl6 — Работа с типами данных
10. Perl6 — Обработка исключений
В прошлой статье мы говорили об отлове исключений, что происходило в специальном вложенном блоке CATCH. На самом деле этот блок является особой разновидностью — Phasers (я просто не могу перевести это слово). Phasers — это специальные вложенные блоки, которые выполняются при определенных условиях. С чем же их готовить увидите под катом.

Рассмотрим основных представителей Phaser'ов:

  • BEGIN {...}*
    Данный блок выполняется единожды при компиляции приложения, причем последний вычисленный результат (как и в дальнейшем у блоков отмеченных звездочкой) может быть использован как результат для присвоения переменной:
    say "Test say";
    
    my $block = BEGIN {
      say "Compile say";
      "Compile time " ~ now;
    }
    
    say $block;
    

    В данном случае переменной блок будет присваиваться строка отражающая время компиляции (Причем как я понял сам блок выполняется при компиляции, а при выполнении скрипта переменной присваивается уже вычисленного значения).
    В результате мы увидим на экране:

    Compile say
    Test say
    Compile time Instant:1377026323.088
    

    Причем, если внутри блока BEGIN произойдет исклчение:

    say "Test say";
    
    my $block = BEGIN {
      die "Compile die";
      "Compile time " ~ now;
    }
    
    say $block;
    

    То на экране мы уже не увидим надписи «Test say», а сразу «Compile die», хотя вывод и происходит раньше чем присвоение результата выполнения блока BEGIN.
    Ну и последний пример по этому блоку, чтобы развеять возможные сомнения:

    say "Test say";
    
    BEGIN {
      say "Compile say";
    }
    
    say "Test say 2";
    
    BEGIN {
      say "Compile say 2";
    }
    

    В результате мы увидим на экране:

    Compile say
    Compile say 2
    Test say
    Test say 2
    

  • CHECK {...}*
    Аналогично предыдущему, но действия выполняются после выполнения компиляции но до выполнения самого скрипта, однако как я заметил в обратном порядке следования в коде:

    say "Test say";
    
    BEGIN {
      say "Compile say";
    }
    
    CHECK {
      say "Check say";
    }
    
    say "Test say 2";
    
    BEGIN {
      say "Compile say 2";
    }
    
    CHECK {
      say "Check say 2";
    }
    

    В результате мы увидим на экране:

    Compile say
    Compile say 2
    Check say 2
    Check say
    Test say
    Test say 2
    

  • INIT {...}*
    Данный блок выполняется при запуске скрипта:

    say "Test say";
    
    BEGIN {
      say "Compile say";
    }
    
    CHECK {
      say "Check say";
    }
    
    INIT {
      say "Init say";
    }
    
    say "Test say 2";
    
    BEGIN {
      say "Compile say 2";
    }
    
    CHECK {
      say "Check say 2";
    }
    
    INIT {
      say "Init say 2";
    }
    

    В результате вывод следующий:

    Compile say
    Compile say 2
    Check say 2
    Check say
    Init say
    Init say 2
    Test say
    Test say 2
    

  • END {...}
    Блок выполняется при остановке скрипта. Стоит отметить, что выполняется он так же как и в случае с CHECK в обратном порядке как были встречены в скрипте, и ещё одну важную вещь — данный блок выполняется даже если в скрипте произошло исключение:

    say "Test say";
    
    BEGIN {
      say "Compile say";
    }
    
    CHECK {
      say "Check say";
    }
    
    INIT {
      say "Init say";
    }
    
    END {
      say "End say";
    }
    
    say "Test say 2";
    
    BEGIN {
      say "Compile say 2";
    }
    
    CHECK {
      say "Check say 2";
    }
    
    INIT {
      say "Init say 2";
    }
    
    END {
      say "End say 2";
    }
    
    die "test die";
    

    В результате видим на экране:

    Compile say
    Compile say 2
    Check say 2
    Check say
    Init say
    Init say 2
    Test say
    Test say 2
    test die
      in block  at blablablaperl.p6:37
    
    End say 2
    End say
    

  • ENTER {...}*
    Данный блок выполняется при любом выполнении блока, в который вложен блок ENTER:

    loop (my $i=0; $i <4; $i++)
    {
       ENTER {
          say $i;
       }
       say "Another loop entry";
    }
    
    ENTER {
       say "Test Enter";
    }
    

    Вывод данного скрипта:

    Test Enter
    0
    Another loop entry
    1
    Another loop entry
    2
    Another loop entry
    3
    Another loop entry
    

    Стоит также отметить то, что можно блоки Phasers вкладывать друг в друга.

  • LEAVE {...}
    Выполняется при завершении работы блока, в который LEAVE был вложен. Вот тут вот я и сново столкнулся с проблемами, из-за которых я и отложил в прошлый раз изучение — я опять начал получать невнятные ошибки если поставил не туда пробел. А пример с блоком LEAVE у меня просто напросто не скомпилировался, оповещая меня о ошибке с неправильным количеством параметров у оператора return, который я кстате и не пытался использовать. Но пример я всетаки приведу:

    loop (my $i=0; $i <4; $i++)
    {
       LEAVE {
          say "Test leave";
       }
       say "Another loop entry";
    }
    

    Если вдруг кто-нибудь сможет это скомпилировать, поведайте пожалуйста в комментариях.
    Как в дальнейшем я выяснил, не работает часть, связанная с выходом из блока. Но перейдем дальше:

  • KEEP {...}
    Данный блок выполняется при успешном выполнении всего блока (как именно определяется успешность проверить не смог, так как скрипт с использованием такого блока не компилируется).
  • UNDO {...}
    Аналогично предыдущему блоку, но в случае если выполнение было завершено не успешно. Пример тоже не компилируем.
  • FIRST {...}*
    Данный блок выполняется только при первом запуске блока, в который вложен FIRST:

    loop (my $i=0; $i <4; $i++)
    {
       FIRST {say "Test keep";}
       say "Another loop entry";
    }
    

    Соответственно вывод:

    Test keep
    Another loop entry
    Another loop entry
    Another loop entry
    Another loop entry
    

Так же в отдельный список выведены операторы, которые действуют подобно Phaser'ам, которые пишутся в нижнем регистре:

  • do {...}*
    Выполняет все операции в блоке, а результат представляет в виде терма. Пример использования:

    if ( do {my $a = 10; $a = $a*2;} == 20)
    {
       say "It's ok";
    }
    

    Таким образом мы вложили вычисление внутрь блока if, а сам блок do можно будет использовать потом для сравнения. Пример конечно очень плохой для ваших пальцев (можно и линейкой схлопотать по ним за такое), но для того чтобы показать для чего он может быть использован пример сгодится.

  • once {...}*
    Данный блок должен будет выполниться лишь раз. К сожалению проверить я так и не смог, так как пример не компилируем:

    my $i = once {
       say "Test once";
    };
    
    $i();
    $i();
    $i();
    

    Жалуется на то, что не смог найти функцию &once.

  • try {...}*
    Знакомый по предыдущей статье блок, игнорирующий все исключения.
  • lazy {...}*
    По описанию должен возвращать «обещание» о выполнении. К сожалению результат для него как и у 'once' блока. Как я понял данный блок должен помочь нам в организации «ленивых вычислений».
  • async {...}
    Выполняет блок в новом потоке. К сожалению опять же не смог скомпилировать пример. Зато теперь имею представление о том как будет происходить работа с потоками.

Чтож, пожалуй пора заканчивать. Как-то даже длинновай пост получился из-за примеров, но надеюсь вам понравилось.

Автор: WarFair

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js