Самая большая проблема в том, что вы использовали *
для создания лямбда-кода Wh WhirlCode и m
.
m/…/
сопоставляется с тем, что происходит в $_
.
Это эффективно происходитза пределами ~~
.
Если бы вы использовали только m/…/
, это сработало бы
sub MAIN(
Str :$r where m:i/< aaa bbb ccc >/ = "bbb"
) { say $r }
Вы могли бы также поставить :i
внутри регулярного выражения
/:i < aaa bbb ccc >/
Предложение where
выполняет интеллектуальное сопоставление, как ~~
выполняет интеллектуальное сопоставление.Таким образом, использование обоих, как и вы, является излишним.
В функции на основе smartmatch выражение, запускаемое с $_
, устанавливается на соответствующее значение.Результат этого выражения затем сопоставляется с вводом.
Я собираюсь использовать subset
, чтобы попытаться помочь объяснить его лучше
subset Foo of Str where * ~~ m:i/< aaa bbb ccc >/;
Когда вы совпадаете с нимпервое, что происходит, это проверка Str
.
(это довольно эффективно, и специалист по типу может устранить эту проверку)
'ccc' ~~ Str; # Str.ACCEPTS('ccc');
Затем выражение в where
предложение запускается со значением, проверяемым в $_
.
my $result = do given 'ccc' { * ~~ m:i/< aaa bbb ccc >/ }
# $result holds a closure
Что происходит дальше, это то, что результат сопоставляется с проверяемым значением.
'ccc' ~~ $result; # $result.ACCEPTS('ccc');
Последнее в этом случае будет зависеть от того, что было в $_
в то время.
Поскольку теперь это произойдет глубоко внутри языка, вы можете не иметь никакого контроля над $_
.
В случае предложения where
это может быть что угодно.
$_ = 'fubar';
# v--v
subset Foo where * ~~ m:i/
{ say '$_ = ', $_ }
{ say 'orig = ', $/.orig } # the string that is matched against
< aaa bbb ccc >
/;
my $result = 'ccc' ~~ Foo;
# $_ = fubar
# orig = fubar
say $result;
# False
Поскольку 'fubar'
не соответствует /< aaa bbb ccc >/
, результат равен False
.
Добавив * ~~
, вы также добавили "жуткое действие на расстоянии".
Это шorks без * ~~
, потому что Regex.ACCEPTS()
не зависит от $_
.
$_ = 'fubar';
subset Foo where m:i/
{ say '$_ = ', $_ }
{ say 'orig = ', $/.orig }
< aaa bbb ccc >
/;
my $result = 'ccc' ~~ Foo;
# $_ = fubar
# orig = ccc
say $result
# True
Существует причина, по которой Perl 6 выполняет два уровня выполнения кода для кода, подобного следующему
subset Bar where $_ eq any < aaa bbb ccc >;
my $result = do given 'ccc' { $_ eq any < aaa bbb ccc > }
# $result = True;
# 'ccc' ~~ $result;
$result.ACCEPTS('ccc');
# $result is True, and True.ACCEPTS() always returns True
Обратите внимание, что его можно сократить до:
subset Bar where any < aaa bbb ccc >;
my $result = do given 'ccc' { any < aaa bbb ccc > }
# $result = any < aaa bbb ccc >;
# 'ccc' ~~ $result;
$result.ACCEPTS('ccc');
# any(< aaa bbb ccc >).ACCEPTS('ccc')
Это двойное выполнение кода происходит для всех функций интеллектуального сопоставления.
~~
'ccc' ~~ $_ eq any < aaa bbb ccc > # True.ACCEPTS('ccc')
'ccc' ~~ any < aaa bbb ccc > # any(…).ACCEPTS('ccc')
where
subset Baz where $_ eq any < aaa bbb ccc >
subset Baz where any < aaa bbb ccc >
when
when $_ eq any < aaa bbb ccc > {…}
when any < aaa bbb ccc > {…}
По сути, это так, что вы можете smartmatch против значения или против выражения или против кода.
(Код на самом деле является типом значения в Perl 6)
10 ~~ 0..10; # match against a value
10 ~~ Range.new(0,10); # same as previous line
10 ~~ 0 ≤ * ≤ 10; # match against code
10 ~~ -> $_ { 0 ≤ $_ ≤ 10 } # basically the same as previous line
10 ~~ 0 ≤ $_ ≤ 10; # match against an expression with $_
# (not the same a previous two lines)
Я хочу отметить, что регулярные выражения в Perl 6 являются типом функции.
my &foo = sub ($_) {$_ eq 'abc'};
my &bar = * eq 'abc';
my &baz = /^ abc $/;
my &zzz = 'abc' # ERROR
Итак, * ~~ /…/
создает функцию из чего-то, что уже является функцией.
Она также поворачивает то, чтобудет двойное выполнение кода в четырехкратное выполнение кода.
В m/…/
m
эффективно заставляет регулярное выражение / функцию работать с whatevЭто происходит в $_
.
# $_ = Any; # initial value in $_
my &code = * ~~ m/abc/;
my &code = * ~~ ($_ ~~ /abc/); # same as previous line
Существует также rx
, что похоже на m
, за исключением того, что оно всегда возвращает само регулярное выражение, а не результат его вызова.(Голый /…/
действует как rx/…/
)
Смарт-сравнение может сбивать с толку, когда вы только начинаете.
Я бы сказал, что это может сбить с толку людей, которые в других отношениях являются экспертамиPerl 6.
(это все еще немного сбивает меня с толку, и я знаю, как это работает.)
Я тоже плохо справился, пытаясь объяснить это здесь, но я пытался соответствовать вашему вопросу, ииспользование вами ~~
усложнило объяснение.
Чтобы быть в здравом уме, я стараюсь следовать нескольким основным правилам.
Они применяются к ~~
, where
и when
.
Используйте литерал или, по возможности, аналогично литералу.
… ~~ 42
… ~~ 'a'
… ~~ any < aaa bbb ccc >
… ~~ 1..10 # not actually a literal, but literal-like
Если вы используете выражение, убедитесь, что оно может возвращать толькоTrue
или False
.
Сопоставляемое значение находится в $_
, что может быть полезно в предложении where
для subset
.
… ~~ 0 < $_
… ~~ $_.lc.contains('abc'); # Returns True or False
when $_.lc.contains('abc') {…}
… where $_.lc.contains('abc');
… ~~ $_.chars.Bool
… ~~ ?$_.chars # prefix:« ? » coerces to Bool
… ~~ ?~$_ # coerce to Str, coerce to Bool
# True if the Str isn't empty
# (so has the same effect as previous two examples)
Если бы я только чтоиспользуется $_.chars
, оно будет совпадать только в том случае, если значение численно совпадает с длиной.
'1' ~~ $_.chars; # True
'3.0' ~~ $_.chars; # True
'1.0' ~~ $_.chars; # False (1.0 == 3)
# that previous one is the same as
do given '1.0' { $_.chars }.ACCEPTS( '1.0' ) # False
# 3.ACCEPTS('1.0')
Поэтому я рекомендую убедиться в этомвозвращает Bool.
Из этого правила есть исключение.А именно, вызов подпрограммы, которая возвращает значение, с которым вы хотите выполнить интеллектуальное сопоставление.
… ~~ Date.today.day-of-week;
(Это плохой пример, но иллюстрирует, что я имею в виду.)
Использовать вызываемый элемент.
Это эффективно удаляет первый (выражение) слойВыполнение кода.
(Результирующее значение равно значению результата функции.)
… ~~ *.lc.contains('abc')
… ~~ {.lc.contains('abc')}
… ~~ /:i abc/ # remember that a regex is a function
when {.lc.contains('abc')} {…}
… where {.lc.contains('abc')};
sub foo ( $_ ) { .lc.contains('abc') }
… ~~ &foo
when &foo {…}
… where &foo;
Не используйте ~~
ни в одной из двух других функций интеллектуального сопоставления..
when * ~~ /…/ {…} # don't do this
… where * ~~ /…/ # don't do this either
… where $_ ~~ /…/ # No, … just no.
Я был бы немного более снисходительным, если бы было длинное выражение, и это только его часть.
when (.chars = 3 ?? $_ ~~ Str !! $_ ~~ Int) {…}
Я никогда не сталкивался среальный код, где это было бы полезно.
Каждый раз, когда я видел, как ~~
использовался в smartmatch, он работал бы лучше без него.
Придерживаясьвышеуказанные правила m:i/…/
все равно будут работать, но по другой причине.
'ccc' ~~ m:i/ < aaa bbb ccc > /;
my $result = do given 'ccc' { m:i/ < aaa bbb ccc > / }
say $result.perl;
# Match.new(pos => 3, orig => "ccc", hash => Map.new(()), from => 0, list => (), made => Any)
$result = $result.ACCEPTS('ccc');
say $result.perl;
# Match.new(pos => 3, orig => "ccc", hash => Map.new(()), from => 0, list => (), made => Any)
.ACCEPTS()
в экземпляре Match всегда возвращает себя.Это также всегда Trueish.
По-прежнему работает правильно, если не удается найти соответствие.(Возвращает что-то, что является ложным.)
Снова ограничение where
и условие when
совпадают с правой стороной ~~
.