insaniac has asked for the wisdom of the Perl Monks concerning the following question:
hey guys,
I'm playing with the map sub, I just recently discovered all the need tricks you can do with.
but since i'm posting here, i must be stuck at some point.
please have a look at the following:
perl -e '@d=([0,"BE"],[3,"BUS"],[4,"BUS2"]); @a=map($_ , split(/,/, "B
+US2,BE") ) ; @b=map { my $mapkey=$_; map { [@{$d[$mapkey]}] if $d[$ma
+pkey]->[1] =~ /$a[$_]/ } 0..$#a } 0..$#d; use Data::Dumper;print Dump
+er(@b);'
$VAR1 = '';
$VAR2 = [
0,
'BE'
];
$VAR3 = '';
$VAR4 = '';
$VAR5 = [
4,
'BUS2'
];
$VAR6 = '';
or more readable:
my @d=([0,"BE"],[3,"BUS"],[4,"BUS2"]); # some default values
my @a=map($_ , split(/,/, "BUS2,BE") ) ; # a customer setting for
+ instance
my @b=map {
my $mapkey=$_;
map {
[@{$d[$mapkey]}] if $d[$mapkey]->[1] =~ /$a[$_]/
} 0..$#a
} 0..$#d; # @b will contain stuff that nee
+ds to be configured
use Data::Dumper;
print Dumper(@b);'
__END__
gives the following output:
$VAR1 = '';
$VAR2 = [
0,
'BE'
];
$VAR3 = '';
$VAR4 = '';
$VAR5 = [
4,
'BUS2'
];
$VAR6 = '';
The above code is a test scenario ofcourse.
What I want to achieve is that @b contains only the anonymous arrays which are found in @d, so i don't want the undef entries.
Any help is greatly appreciated...
Cheers!
--
to ask a question is a moment of shame
to remain ignorant is a lifelong shame
Re: map weirdness
by ysth (Canon) on Dec 13, 2004 at 12:49 UTC
|
I'm guessing you need to change this part: [@{$d[$mapkey]}] if $d[$mapkey]->[1] =~ /$a[$_]/
That generates an undef when the match fails, and shallow-copies the arrayref $d[$mapkey] when the match succeeds. Sounds like you want something more like (untested):
$d[$mapkey]->[1] =~ /$a[$_]/ ? [@{$d[$mapkey]}] : ()
to provide an empty list when the match fails. Using ?: in a map is a common idiom. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] |
Re: map weirdness
by jonadab (Parson) on Dec 13, 2004 at 13:00 UTC
|
What I want to achieve is that @b contains only the anonymous arrays which are found in @d, so i don't want the undef entries.
I can think of two major ways to obtain that result.
One way would be if you leave map like it is but
then use grep to filter out the undesired results:
@b = grep { ref $_ } @b;
The other way would be to change the map so that
it only returns what you want in the first place,
something along these lines...
my @b=map {
my $mapkey=$_;
map {
($d[$mapkey]->[1] =~ /$a[$_]/)
? [@{$d[$mapkey]}] : ()
} 0..$#a
} 0..$#d;
(This code is all untested.)
The first approach is probably easier to follow,
if you're new to Perl. The second approach is
more direct, however, probably more efficient,
and definitely more concise.
Of course, if I were writing
the thing, I'd get rid of those array indices
and map over the lists themselves...
my @b = map {
my $d = $_;
map { ($d->[1] =~ $$_) ? $d : () } @a
} @d
And if by =~ you really mean eq, we can simplify
this further using a hash:
my %a = map { $_ => 1 } @a;
my @b = map {
($a{$_->[1]}) ? $_ : ()
} @d
Update: fixed a variable there.
Note that this last one doesn't do exactly the same
thing. With your sample data set it gets the same
result, and I *suspect* that it actually does what
you want, but now if @a were to contain "BUS",
@d would no longer get [4,"BUS2"],
only [3,"BUS"]. But it exorcises
the nested map, which makes it easier to follow
and more efficient, if it does
what you want.
"In adjectives, with the addition of inflectional endings, a changeable long vowel (Qamets or Tsere) in an open, propretonic syllable will reduce to Vocal Shewa. This type of change occurs when the open, pretonic syllable of the masculine singular adjective becomes propretonic with the addition of inflectional endings."
— Pratico & Van Pelt, BBHG, p68
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Excellent post, but I feel like I need to write in defense of the "first approach." Not only is it "easier to follow, if you're new to Perl," but I think it's easier to follow even if you're not new to Perl. In other words, it's simply clearer code.
I say this because map and grep have fairly common, specific uses. Nearly anyone reading the code will immediately recognize grep as filtering the results. Shoving that logic into the map block fails to take advantage of that quick recognition. It may not seem like a lot, but these things add up.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
I feel like I need to write in defense of the "first approach." Not only is it "easier to follow, if you're new to Perl," but I think it's easier to follow even if you're not new to Perl.
Up to a point, I'd agree. Indeed, there's a reason
I gave both approaches. Yet, you'll notice that it
was when I went to the second approach that I felt
the need to simplify the map (getting rid of the
array indices -- this is Perl, not C), and then only
after I'd done that did I really understand the logic
of what was going on well enough to construct my
fourth solution, which removes the nested map
altogether. Having done that, we could combine
it with the grep approach...
my %a = map { $_ => 1 } @a;
my @b = grep { $a{$_->[1]} } @d
This is perhaps clearest of all, if it does
the right thing, which I suspect it does. I
find it hard to believe that the original
poster actually wanted the side-effect of
matching substrings in this case.
"In adjectives, with the addition of inflectional endings, a changeable long vowel (Qamets or Tsere) in an open, propretonic syllable will reduce to Vocal Shewa. This type of change occurs when the open, pretonic syllable of the masculine singular adjective becomes propretonic with the addition of inflectional endings."
— Pratico & Van Pelt, BBHG, p68
| [reply] [Watch: Dir/Any] [d/l] |
Re: map weirdness
by zejames (Hermit) on Dec 13, 2004 at 13:09 UTC
|
By the way, you are using
[@{$d[$mapkey]}]
which means that you have a array ref, $d{$mapkey} that you derefence through @{$d[$mapkey]}, and then you want to have the corresponding array ref. This is not useful, just use :
$d[$mapkey]
HTH
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Those aren't the same; try:
$, = " ";
$d[3] = [qw/just another perl hacker/];
$copy1 = $d[3];
$copy2 = [@{$d[3]}];
print "\$d[3] was", @{$d[3]}, "\n";
$d[3][2] =~ y/p/P/;
print "\$d[3] now", @{$d[3]}, "\n";
print "\$copy1 is", @$copy1, "\n";
print "\$copy2 is", @$copy2, "\n";
__END__
$d[3] was just another perl hacker
$d[3] now just another Perl hacker
$copy1 is just another Perl hacker
$copy2 is just another perl hacker
The [@{}] syntax copies the array elements into a new array, without it, you are sharing the same array. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
well, it works in my case..
insaniac][amano: ~ : perl -we 'use strict;my @d=([0,"BE"],[3,"BUS"],[4
+,"BUS2"]);my @a=map($_ , split(/,/, "BUS2,BE") ) ; my @b=map { my $ma
+pkey=$_; map { $d[$mapkey]->[1] =~ /$a[$_]/ ? $d[$mapkey] : () } 0..$
+#a } 0..$#d; use Data::Dumper;print Dumper(@b);'
$VAR1 = [
0,
'BE'
];
$VAR2 = [
4,
'BUS2'
];
insaniac][amano: ~ : perl -we 'use strict;my @d=([0,"BE"],[3,"BUS"],[4
+,"BUS2"]);my @a=map($_ , split(/,/, "BUS2,BE") ) ; my @b=map { my $ma
+pkey=$_; map { $d[$mapkey]->[1] =~ /$a[$_]/ ? [@{$d[$mapkey]}] : () }
+ 0..$#a } 0..$#d; use Data::Dumper;print Dumper(@b);'
$VAR1 = [
0,
'BE'
];
$VAR2 = [
4,
'BUS2'
];
--
to ask a question is a moment of shame
to remain ignorant is a lifelong shame
| [reply] [Watch: Dir/Any] [d/l] |
|
|
|
That's not necessarily true. Doing this creates a new arrayref with the same contents, but if down the line he's manipulating the map'd contents but wants to be able to run more results through the same process he may very well want to have copied the original data (since if he alters the map'd contents down the line it'll affect what he's using to build the new list). It is more inefficient than just using the arrayref, but depending on what he's trying to do it might be correct.
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] |
|
|
Actually, [@{$d[$mapkey]}] and $d[$mapkey] are not the same. The first makes a copy, the second doesn't.
#!/usr/bin/perl
use strict;
use warnings;
my @d = ([0, 1, 2], [3, 4, 5], [6, 7, 8]);
my $mapkey = 1;
sub show {
local($") = ", ";
print("[", join(", ", map({"[@$_]"} @d)), "]\n");
}
my $one = [@{$d[$mapkey]}];
my $two = $d[$mapkey];
show; # Show array. Unmodified.
$one->[1] = "foo"; show; # Array is still unmodified.
$two->[1] = "foo"; show; # This modifies the array.
__END__
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]
[[0, 1, 2], [3, foo, 5], [6, 7, 8]]
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: map weirdness
by Animator (Hermit) on Dec 13, 2004 at 16:34 UTC
|
Any particular reason you use: my @a=map($_ , split(/,/, "BUS2,BE") ) ;and not my @a = split /,/, "BUS2,BE";?
(Update: typo) | [reply] [Watch: Dir/Any] [d/l] [select] |
|
|