Вызов Bool на Regex не работает как описано - PullRequest
5 голосов
/ 29 июня 2019

Согласно документации , Bool метод Regex class ...

Сопоставляется с переменной $ _ вызывающей стороны и возвращает True для совпадения илиFalse для отсутствия совпадения.

Однако в этом примере

$_ = "3";
my regex decimal { \d };
say &decimal.Bool;

возвращает False.Кроме того, при взгляде на источник , в некотором роде имеет смысл то, что он говорит, поскольку он будет соответствовать переменной экземпляра $!topic.Не ясно, однако, что эта переменная будет эффективно соответствовать $ _, и приведенный выше пример, кажется, говорит об этом.Есть идеи о том, что на самом деле происходит?

1 Ответ

9 голосов
/ 29 июня 2019

Краткий ответ: документация была довольно точной для 6.c, однако точная семантика была вовсе не такой простой, как у «вызывающего» (и на самом деле содержала риск действительно странных ошибок). Уточненное поведение:

  • Анонимные регулярные выражения, построенные в таких формах, как /.../ и rx:i/.../, будут захватывать $_ и $/ в той точке, в которой они достигнуты в коде (заполняя переменную $!topic, упомянутую в вопросе).
  • Bool и sink вызовут совпадение с захваченным $_ и сохранят полученный объект Match в этом $/, при условии, что он доступен для записи.

Поскольку это относится только к анонимным регулярным выражениям, вам нужно написать:

$_ = "3";
my regex decimal { \d };
say /<&decimal>/.Bool;

Вот длинный ответ. Целью поведения Bool -causes-match в первую очередь было то, чтобы сработали такие вещи:

for $file-handle.lines {
    .say if /^ \d+ ':'/;
}

Здесь цикл for заполняет переменную темы $_, а if предоставляет логический контекст. Первоначальный дизайн состоял в том, что .Bool будет выглядеть на $_ звонящего. Однако с этим было несколько проблем. Рассмотрим:

for $file-handle.lines {
    .say if not /^ \d+ ':'/;
}

В этом случае not является вызывающим .Bool на Regex. Однако, not также будет иметь свой собственный $_, который - как и в любой подпрограмме - будет инициализирован до Any. Таким образом, теоретически, сопоставление не будет работать. Помимо этого, потому что фактически было реализовано, чтобы пройти через вызывающих, пока один не был найден с $_, который содержит определенное значение! Это так плохо, как кажется. Рассмотрим случай, подобный следующему:

sub foo() {
    $_ = some-call-that-might-return-an-undefiend-value();
    if /(\d+)/ {
        # do stuff
    }
}
$_ = 'abc123';
foo();

В случае, если вызов внутри foo должен был вернуть значение, не являющееся зависимым - возможно, неожиданное - сопоставление продолжило бы обход цепочки вызывающих абонентов и вместо этого нашло бы значение $_ в вызывающей стороне foo. На самом деле мы могли бы пройти много уровней в стеке вызовов! (Помимо: да, это также означало, что были сложности, из-за которых $/ тоже нужно обновлять с результатами!)

Предыдущее поведение также требовало, чтобы $_ имел динамическую область видимости, то есть, был доступен для вызывающих абонентов для поиска. Однако переменная, имеющая динамическую область действия, предотвращает многочисленные анализы (как компилятора, так и программиста) и, следовательно, оптимизацию. Со многими идиомами, использующими $_, это казалось нежелательным (никто не хочет видеть руководства по производительности Perl 6, предлагающие «не используйте with foo() { .bar } в горячем коде, используйте вместо него with foo() -> $x { $x.bar }»). Таким образом, 6.d изменил $_ на регулярную лексическую переменную.

Это изменение области действия 6.d $_ имело довольно незначительные последствия в реальном мире, но оно вызвало семантику .Bool и .sink на Regex, поскольку они были единственной часто используемой вещью. это полагалось на $_, являющийся динамическим. Это, в свою очередь, пролило свет на «первое определенное $_» поведение, которое я только что описал, - в этот момент использование динамического определения области видимости стало больше представлять опасность, чем выгоду!

Новая семантика означает, что программист, пишущий анонимное регулярное выражение, может полагаться на него, сопоставляя $_ и обновляя $/, которые видны в области, в которой они написали регулярное выражение - что кажется более простым для объяснения, и - в в случае, если они получат $_, который не определен - гораздо менее удивительно!

...