From the examples below, I see that / /
and m/ /
aren't exactly synonymous, contrary to what I expected. I thought that the only reason to use m/ /
instead of / /
was that it allows using different delimiters (e.g. m{ }
). Why are they different and why would I want to use one versus the other?
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 $ / );
["SampleSheet.csv".IO]
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
I found that I could fix this by putting a block around the matching expression:
> my @csv_files = dir( test => { m:i/ csv $ / } );
["SampleSheet.csv".IO]
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 $ / } );
[]
> my @csv_files = dir( test => { m/ csv $ / } );
["SampleSheet.csv".IO]
Then I found out that if I used the case-insensitive adverb inside / /
, it does work:
> my @csv_files = dir( test => /:i csv $ / );
["SampleSheet.csv".IO]
Anyway, / /
and m/ /
are clearly behaving differently and it's not yet clear to me why.
The difference between
/.../
andm/.../
From Regexes#Lexical conventions:
In other words, it's
/.../
andrx/.../
that are synonyms, not/.../
andm/.../
:/.../
andrx/.../
return the specified regex as aRegex
object, without matching it against anything for now.m/.../
immediately matches the specified regex against the string that's stored in the variable$_
(the so-called "topic"), and returns the result as aMatch
object, or asNil
if there was no match.Demonstration:
Explanations & comments regarding your code
Applying regex modifiers
That code immediately matches the regex against the topic
$_
of the calling scope, which is uninitialized. This involves converting it to a string (which causes the warningUse of uninitialized value of type Any in string context
), and returnsNil
because there is no match. So you're essentially calling the function asdir( test => Nil )
.To make it work, either use
rx
or apply the:i
adverb inside the regex:Blocks as smart-matchers
That works too. What happens here, is:
{ ... }
creates a block that takes a single argument (which is available as$_
inside the block).m:i/ ... /
inside the block matches against$_
, and returns aMatch
.m:i/.../
is the last statement in the block, itsMatch
becomes the return value of the block.test
adverb of thedir
function accepts any smart-matcher, which includes not justRegex
objects but alsoBlock
objects (see the documentation for the smart-match operator~~
).Using a
Regex
as aBool
When a block is used as a smart-matcher, it is first called and then its return value is coerced to a
Bool
:True
means it matched, andFalse
means it didn't.In this case, your block always returs a
Regex
object.Coercing a regex object to a boolean, immediately matches it against the current
$_
, and returnsTrue
if the regex matched, and `False if it didn't:So in your code, the regex ends up being repeatedly checked against
$_
, rather than against the filenames:Filtering files by their extension
This doesn't just find files that have the CSV extension, but all files that end in the three letters
cvs
, including ones likefoobarcsv
orfoobar.xcsv
.Here are two better ways to write it if you only want CSV files:
Or the case-insensitive version: