КОНТРОЛЬ или один раз портит последний? - PullRequest
5 голосов
/ 27 июня 2019

Этот цикл никогда не останавливается:

class CX::Vaya does X::Control {
    has $.message
}

loop {
    once {
        CX::Vaya.new( message => "I messed up!" ).throw;
    }
    last;
    CONTROL {
        default {
            say "Controlled { .^name }: { .message }"
        }
    }
}

Он продолжает записывать

Controlled CX::Last: <last control exception>
Controlled CX::Last: <last control exception>
Controlled CX::Last: <last control exception>
Controlled CX::Last: <last control exception>
Controlled CX::Last: <last control exception>
Controlled CX::Last: <last control exception>
Controlled CX::Last: <last control exception>
Controlled CX::Last: <last control exception>
...

Это может быть бит once, потому что фазер CONTROL с последним завершает работу:

loop { say "Hey"; last; CONTROL { default: .message.say } }
# OUTPUT: «Hey␤<last control exception>␤»

Но я не совсем уверен.

Ответы [ 3 ]

13 голосов
/ 27 июня 2019

Управление циклическим потоком в Perl 6 реализовано с использованием исключений управления.Таким образом, last фактически выдает CX::Last управляющее исключение.Поскольку в блоке CONTROL используется default, он перехватывает CX::Last, выброшенный last, что означает, что элемент управления никогда не передается из цикла.

Исправление состоит в том, чтобы вместо этого указать, какой элемент управленияисключение, чтобы поймать, используя when:

loop {
    once {
        CX::Vaya.new( message => "I messed up!" ).throw;
    }
    last;
    CONTROL {
        when CX::Vaya {
            say "Controlled { .^name }: { .message }"
        }
    }
}
7 голосов
/ 27 июня 2019

Что говорит Джонатан.

Более подробно:

  • Поток управления по умолчанию для P6 состоит в том, что следующий оператор следует за текущим.Поэтому say 42; say 99; по умолчанию выполняет say 42, а затем say 99.

  • P6 имеет расширенную и распространенную систему исключений, которая используется для любых исключений из этого потока управления по умолчанию:один-заявление-то-заместитель немедленно следующие за-заявление.Это не только для ошибок.

  • Любое исключение из потока управления по умолчанию называется исключением.Многие из них являются исключениями ошибок.Но есть и другая категория, а именно исключения потока управления, или исключения управления для краткости.Например, явное или неявное return / leave из подпрограммы / блока является исключением управления.(В принципе. На самом деле компилятор / оптимизатор исключает некоторые из этих механизмов, когда это уместно.)

  • A last - исключение управления.Выдает управляющее исключение CX::Last.Полезная нагрузка сообщения - "last control exception".Вы видите это сообщение.

  • Блок CONTROL вводится, если исключение управления применяется к содержащему его блоку.Он может обрабатывать переданное исключение или не обрабатывать его.Если он обрабатывает его, управление возобновляется с оператора, следующего за ним.Если, как в этом примере, блок CONTROL находится в конце цикла, то следующий оператор будет неявным next (еще одно исключение управления), и цикл будет перезапущен.

  • Код CONTROL { default { say "Controlled { .^name }: { .message }" } обрабатывает все управляющие исключения, включая одно, выданное last.Таким образом, он печатает свое сообщение, а затем цикл продолжается.(Бесконечно.)

  • В коде loop { say "Hey"; last; CONTROL { default: .message.say } } # OUTPUT: «Hey␤<last control exception>␤» блок CONTROL отображает сообщение об исключении, но не обрабатывает Это.(default: - это просто метка, не похожая, скажем, на I'm-a-label: и не связанная с default { ... }.) Таким образом, исключение элемента управления last не обрабатывается вашим кодом, а обрабатывается обработкой языка по умолчаниюкоторый должен вырваться из цикла.

4 голосов
/ 27 июня 2019

Это действительно комбинация CONTROL и CX::Succeed, которая предотвращает поведение по умолчанию last.


Все функции управления потоком реализованы в виде исключений.

Вместо того, чтобы смешивать их с регулярными исключениями в CATCH, для них есть специальный обработчик с именем CONTROL.

И CATCH, и CONTROL можно рассматривать какспециальные формы given.
Два основных различия заключаются в том, что в теме задано исключение и что что-то особенное происходит, если было CX::Succeed.

Если существует CX::Succeedзатем это означает, что исключение или управление потоком было успешно обработано, и что дальнейшая обработка не требуется.


Существует два способа получить CX::Succeed внутри CONTROL.

  1. default block
  2. when block

(Технически я думаю, что просто succeed также должен работать, но это не кажетсяк.)

Если вы находитесь в одном из этих блоков, и выЕсли вы хотите продолжить обработку, вы можете использовать proceed (CX::Proceed).

loop {
    once {
        CX::Vaya.new( message => "I messed up!" ).throw;
    }
    last;

    CONTROL {
        default {
            .^name.say;
            proceed if CX::Last;
        }
    }
}

Гораздо лучше просто использовать блок when, который захватывает именно то, что вы хотите обработать специально.
(Обратите внимание, что постфикс when не выбрасывает CX::Succeed, поэтому в этом случае обычная обработка будет продолжаться.)


default - префикс блока.Это означает, что за ним всегда должен следовать блок.

Добавление только : в конец идентификатора используется только для создания метки, а при вызовах методов - для удаления скобок.

Так что default: здесь абсолютно ничего не делает.

Также once - это , а не префикс блока.Это префикс заявления.(Так уж получилось, что блок можно рассматривать как оператор.)
Нет смысла добавлять туда блок, если вы не собираетесь писать более одного оператора внутри него.


loop {
    once CX::Vaya.new( message => "I messed up!" ).throw;
    last;

    CONTROL {
        when CX::Vaya {
            .^name.say;
        }
    }
}
...