how does this work: map used with ternary hook operator and ()
In a previous question, seaworthy asked how to remove the first 5 elements from an array: How do I remove the first five elements of an array?
Among several suggestions, friedo offered this:
my $cnt = 0;
@array = map { ++$cnt < 5 ? ( ) : $_ } @array;
I don't get the ( ) bit. Please could explain how this works to me, because I can't get my head around it?
I know that the ternary hook operator works like this: (if something) ? (then do this) : (otherwise do this)
For example: $a=2; print ($a==2 ? 3 : 4)
# this prints: 3
because we have: ($a==2 ? 3 : 4)
which means: (if $a is equal to 2) ? (then print 3) : (otherwise print 4)
so with friedo's code, first $cnt is increased to 1, then we have:
$cnt < 5 ? ( ) : $_
which means:
if $cnt is less than 5 ? then ( ) : otherwise $_
I can see how the $_ bit works because i sometimes use map like this:
@array = map { $_, "\n" } @array
This copies an element from @array, places the copy into $, then adds a \n newline, then it copies the value in $ back to @array (and it does this with all values in @array so basically it adds newlines to every element in @array)
therefore:
@array = map { if $cnt is less than 5 then ( ) otherwise $_ } @array
means something like:
@array = map { if $cnt is less than 5 then ( ) otherwise copy the element back to @array }
so clearly ( ) means something li开发者_C百科ke 'get rid of it' but i'm just not sure how it works. Please could you explain it?
In a map
, each item from the array is passed into the code block (in $_
) where it can be transformed into some other value. In other words, map
transforms a list.
In this case, we want to throw away values where the count ($cnt
) is less than 5. So how to we make a map
block return "nothing" when that condition is true?
We can't say
my $cnt = 0; @array = map { ++$cnt < 5 ? undef : $_ } @array;
Because then we'd end up with an array that looks like
( undef, undef, undef, undef, undef, 6, 7, 8 ... )
which is not what we wanted.
But returning ( )
instead returns an empty list. Consider push @foo, ( );
or @bar = ( 1, 2, 3, ( ), 4, 5, 6 );
In each of these cases, the empty set of parens is a list of zero items, which doesn't have any affect on the arrays concerned.
The empty list is useful in ternaries where you need to either return a list item or nothing at all. It's also useful to force list context on an expression to get a count:
my $count = ( ) = $str =~ /\d/g;
Here, we put the regex in list context by assigning it to an empty list, giving us the count of digits in the string. Then we assign that empty list to $count
.
Another frequent example of using lists in map
is when you're transforming something into a hash. For example,
my %unique = map { $_ => 1 } @duplicates;
Here each single item in @duplicates
get transformed into a two-element list that looks like ( 'foo' => 1 )
although it's not as obvious since no parens are involved. All the two-item lists then get built up into one big list of alternating keys and values which construct the hash. Let's say you wanted to make this hash but exclude some items. In this case we either need to return a key/value, or nothing. So that's a good chance to use an empty list:
my %filtered_unique = map { some_test( $_ ) ? ( ) : ( $_ => 1 ) } @duplicates;
I know I'm a bit late in the game here, but why not do something simple?
my @truncated = @array[5 .. $#array]
Apparantly you can return a list instead of an element, and map constructs the result by joining those lists and elements. ()
is just the empty list in this case. For more insight, copy-paste the example, and replace ()
by (1, 2, 3)
.
精彩评论