为什么“//”和“m//”不完全是同义词?

9
从下面的例子中,我发现 / /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/ / 明显表现出不同的行为,目前我还不清楚原因。
1个回答

9

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

来自正则表达式#词法约定

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

你的代码的解释和评论

应用正则表达式修饰符

but recently a file ending in Csv showed up. So I tried matching case insensitively

 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 => Nil ) 调用。
要使其工作,可以使用 rx 或在正则表达式内应用 :i 副词:
my @csv_files = dir( test => rx:i/ csv $ / );

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

块作为智能匹配器

I found that I could fix this by putting a block around the matching expression:

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

这也是可行的。具体实现如下:

  • { ... } 创建一个块,该块接受一个参数(可以在块内部使用 $_)。
  • 在块内使用的 m:i/ ... / 标识符对 $_ 进行匹配,并返回一个 Match 对象。
  • 因为 m:i/.../ 是块中的最后一个语句,所以其 Match 对象成为该块的返回值。
  • dir 函数的 test 副词接受任何智能匹配器,其中包括不仅限于 Regex 对象而且还包括 Block 对象(请参阅 智能匹配运算符 ~~ 的文档)。

Regex用作Bool类型

However, if I had used a block around the original expression it doesn't match with the bare / /, but it does with 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

通过文件扩展名过滤文件

I am searching for CSV files in a directory. At first I searched for files ending in csv, thus (all code shown as seen from the 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"

3
感谢您不遗余力地提供了详尽的“代码说明和注释”部分,这样让人更易于理解! - Christopher Bottoms

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接