Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl map block local variable usage

This code compiles a set by way of hash keys of the unique basename stubs in a set of paths.

%stubs = map { $f=basename $_; $f =~ /^([A-Za-z]+[0-9]+)\./ ; $1=>() } @pathlist;

Why do I need the $f references here? I thought I'd be ok with:

%stubs = map { basename; /^([A-Za-z]+[0-9]+)\./; $1=>() } @pathlist;

But I get no match. Am I not permitted to modify $_ in the map block?



For those wondering what the code is doing:

For each $path (@pathlist), it's getting the basename, matching the first letter-number sequence, and then returning the first bracket match as the key on an empty list value. Example:

/some/dir/foo123.adfjijoijb
/some/dir/foo123.oibhobihe
/some/dir/bar789.popjpoj

returns

foo123 => ()
bar789 => ()

After which I use the keys of the map as the set of values so process.

like image 778
Phil H Avatar asked May 19 '11 07:05

Phil H


2 Answers

basename does not default to acting on $_. But you can match against its return value instead of using $f:

%stubs = map { basename($_) =~ /^([A-Za-z]+[0-9]+)\./; $1 => undef } @pathlist;

Note that () in a list doesn't produce an element, it just flattens to nothing; you have to provide a value, even if only undef. With $1 => (), map iterations would alternate producing a key and a value for %stubs.

It's good to always check that your regex succeed before using $1:

%stubs = map { basename($_) =~ /^([A-Za-z]+[0-9]+)\./ ? ($1 => undef) : () } @pathlist;

though if you don't mind the hash values being the empty string instead of undef, you can just make the regex match return the desired list:

%stubs = map { basename($_) =~ /^([A-Za-z]+[0-9]+)()\./ } @pathlist;
like image 190
ysth Avatar answered Sep 21 '22 06:09

ysth


In map and grep, $_ is an alias for the values in the array. If you modify them, you actually modify the values in the array. This is probably not what you want and probably what is going wrong, but to debug print keys %stubs and @pathlist afterwards in both cases and let us know what it says.

Also: File::Basename's basename does not implicitly work on $_. It generates an error for me.

#!/usr/bin/perl
use feature say;
use File::Basename;

@pathlist=("/some/dir/foo123.adfjijoijb","/some/dir/foo123.oibhobihe","/some/dir/bar789.popjpoj");
%stubs1 = map { $f=basename $_; $f =~ /^([A-Za-z]+[0-9]+)\./ ; $1=>() } @pathlist;
say join(',',keys %stubs1);
say "---";
say join(',',@pathlist);
say "---";

%stubs = map { $_=basename $_; /^([A-Za-z]+[0-9]+)\./; $1=>() } @pathlist;
say join(',',keys %stubs);
say "---";
say join(',',@pathlist);
say "---";

%stubs = map {basename; /^([A-Za-z]+[0-9]+)\./; $1=>() } @pathlist;
like image 45
Seth Robertson Avatar answered Sep 19 '22 06:09

Seth Robertson