соответствие строк в параметрах `MAIN ()` - PullRequest
0 голосов
/ 24 января 2019

Я хотел бы ограничить аргумент MAIN() с совпадением строк.Это работает:

sub MAIN(
    Str :$r where * eq any(< aaa bbb ccc >) = "bbb"
) { say $r }

$ perl6 tb.p6 -r="ccc"
ccc

Но это не так:

sub MAIN(
   Str :$r where * ~~ m:i/< aaa bbb ccc >/ = "bbb",
) { say $r }

$ perl6 ta.p6 -r="ccc"
Use of uninitialized value of type Any in string context.
Methods .^name, .perl, .gist, or .say can be used to stringify it to something meaningful.
  in whatevercode  at ta.p6 line 2
Usage:
  ta.p6 [-r=<Str where { ... }>] 

Ответы [ 2 ]

0 голосов
/ 24 января 2019

Самая большая проблема в том, что вы использовали * для создания лямбда-кода 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 совпадают с правой стороной ~~.

0 голосов
/ 24 января 2019

попробуйте это:

sub MAIN(
   Str :$r where * ~~ / 'aaa' | 'bbb' | 'ccc' / = "bbb",
) { say $r }

< aaa bbb ccc > в регулярном выражении не интерполируется как массив, следует использовать как это:

my @a = < aaa bbb ccc >;
say so "aaa" ~~ /@a/;
...