Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how does this work: map used with ternary hook operator and ()

Tags:

perl

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 like 'get rid of it' but i'm just not sure how it works. Please could you explain it?

like image 946
Literat Avatar asked Feb 27 '11 17:02

Literat


2 Answers

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;
like image 89
friedo Avatar answered Oct 24 '22 11:10

friedo


I know I'm a bit late in the game here, but why not do something simple?

my @truncated = @array[5 .. $#array]

like image 2
Gilbert Avatar answered Oct 24 '22 10:10

Gilbert