【问题标题】:Why aren't // and m// exactly synonymous?为什么 // 和 m// 不完全是同义词?
【发布时间】:2017-07-26 12:28:17
【问题描述】:

从下面的示例中,我看到 / /m/ / 并不完全是同义词,这与我的预期相反。我认为使用m/ / 而不是/ / 的唯一原因是它允许使用不同的分隔符(例如m{ })。为什么它们不同?为什么我要使用其中一个而不是另一个?

我正在目录中搜索 CSV 文件。起初我搜索了以csv 结尾的文件,因此(所有代码都显示在 Perl 6 REPL 中):

> my @csv_files = dir( test => / csv $ /  );
["SampleSheet.csv".IO]

但最近出现了一个以Csv 结尾的文件。所以我尝试不区分大小写:

> my @csv_files = dir( test => m:i/ csv $ / );
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 block <unit> at <unknown file> line 1

我发现我可以通过在匹配表达式周围放置一个块来解决这个问题:

> my @csv_files = dir( test => { m:i/ csv $ / } );
["SampleSheet.csv".IO]

但是,如果我在原始表达式周围使用了一个块,它与裸露的 / / 不匹配,但它与 m/ / 匹配:

> my @csv_files = dir( test => { / csv $ / } );
[]
> my @csv_files = dir( test => { m/ csv $ / } );
["SampleSheet.csv".IO]

然后我发现如果我在/ / 中使用不区分大小写的副词,它确实有效:

> my @csv_files = dir( test => /:i csv $ / );
["SampleSheet.csv".IO]

无论如何,/ /m/ / 的行为显然不同,我还不清楚为什么。

【问题讨论】:

    标签: regex raku


    【解决方案1】:

    /.../m/.../的区别

    来自Regexes#Lexical conventions

    m/abc/;         # a regex that is immediately matched against $_ 
    rx/abc/;        # a Regex object 
    /abc/;          # a Regex object
    

    换句话说,/.../rx/.../ 是同义词,而不是 /.../m/.../

    • /.../rx/.../ 将指定的正则表达式作为 Regex 对象返回,暂时不将其与任何内容进行匹配。
    • m/.../ 立即将指定的正则表达式与存储在变量$_(所谓的“主题”)中的字符串进行匹配,并将结果作为Match 对象返回,如果没有匹配则返回为Nil .

    演示:

    $_ = "Foo 123";
    
    say m/\d+/;        # 「123」
    say m/\d+/.^name;  # Match
    
    say /\d+/;         # /\d+/
    say /\d+/.^name;   # Regex
    

    关于您的代码的说明和 cmets

    应用正则表达式修饰符

    但最近出现了一个以 Csv 结尾的文件。所以我尝试不区分大小写

     my @csv_files = dir( test => m:i/ csv $ / );
     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 block <unit> at <unknown file> line 1
    

    该代码立即将正则表达式与未初始化的调用范围的主题$_ 匹配。这涉及将其转换为字符串(这会导致警告Use of uninitialized value of type Any in string context),并返回Nil,因为没有匹配项。因此,您实际上是将该函数称为dir( test =&gt; Nil )

    要使其正常工作,请使用rx 或在正则表达式中应用:i 副词:

    my @csv_files = dir( test => rx:i/ csv $ / );
    
    my @csv_files = dir( test => / :i csv $ / );
    

    块作为智能匹配器

    我发现我可以通过在匹配表达式周围放置一个块来解决这个问题:

    > my @csv_files = dir( test => { m:i/ csv $ / } );
    

    这也有效。这里发生的是:

    • { ... } 创建一个接受单个参数的块(在块内以 $_ 的形式提供)。
    • 块内的m:i/ ... /$_ 匹配,并返回Match
    • 因为m:i/.../是块中的最后一条语句,所以它的Match成为块的返回值。
    • dir 函数的test 副词接受任何智能匹配器,其中不仅包括Regex 对象,还包括Block 对象(请参阅smart-match operator ~~ 的文档)。

    使用Regex 作为Bool

    但是,如果我在原始表达式周围使用了一个块,它与裸 // 不匹配,但它与 m/ / 匹配:

    > my @csv_files = dir( test => { / csv $ / } );
    []
    

    当一个块用作智能匹配器时,首先调用它,然后将其返回值强制为BoolTrue 表示匹配,False 表示不匹配。

    在这种情况下,您的块总是返回一个 Regex 对象。

    将正则表达式对象强制转换为布尔值,立即将其与当前的$_ 匹配,如果正则表达式匹配则返回True,如果不匹配则返回`False:

    say /\d+/.Bool;  # False
    
    $_ = "123";
    say /\d+/.Bool;  # True
    

    因此,在您的代码中,正则表达式最终会被反复检查 $_,而不是文件名:

    $_ = "abc";
    .say for dir test => { / \d+ / }  # Returns no filenames
    
    $_ = "abc 123";
    .say for dir test => { / \d+ / }  # Returns all filenames
    

    按扩展名过滤文件

    我正在目录中搜索 CSV 文件。起初我搜索了以 csv 结尾的文件,因此(所有代码都显示在 Perl 6 REPL 中):

    > my @csv_files = dir( test => / csv $ /  );
    

    这不仅会查找具有 CSV 扩展名的文件,还会查找以三个字母 cvs 结尾的所有文件,包括 foobarcsvfoobar.xcsv 之类的文件。
    如果您只想要 CSV 文件,这里有两种更好的编写方法:

    my @csv-files = dir test => / ".csv" $ /;
    
    my @csv-files = dir.grep: *.extension eq "csv"
    

    或者不区分大小写的版本:

    my @csv-files = dir test => / :i ".csv" $ /;
    
    my @csv-files = dir.grep: *.extension.lc eq "csv"
    

    【讨论】:

    • 感谢您继续努力并提供全面的Explanations &amp; comments regarding your code 部分!
    猜你喜欢
    • 2011-04-09
    • 2011-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-26
    • 2020-09-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多