6.13. Complex Mappings
When a map, grep, or first is applied to a list, the block performing the transformation or conditional test can sometimes become quite complex[*]. For example:
use List::Util qw( max ); Readonly my $JITTER_FACTOR => 0.01; # Jitter by a maximum of 1% my @jittered_points = map { my $x = $_->{x}; my $y = $_->{y}; my $max_jitter = max($x, $y) / $JITTER_FACTOR; { x => $x + gaussian_rand({mean=>0, dev=>0.25, scale=>$max_jitter}), y => $y + gaussian_rand({mean=>0, dev=>0.25, scale=>$max_jitter}), } } @points; This large block is very hard to read, especially since the final anonymous hash constructor looks more like a nested block. So the temptation is to use a for instead: my @jittered_points; for my $point (@points) { my $x = $point->{x}; my $y = $point->{y}; my $max_jitter = max($x, $y) / $JITTER_FACTOR; my $jittered_point = { x => $x + gaussian_rand({ mean=>0, dev=>0.25, scale=>$max_jitter }), y => $y + gaussian_rand({ mean=>0, dev=>0.25, scale=>$max_jitter }), }; push @jittered_points, $jittered_point; } That certainly does help the overall readability, but it's still far from optimal. A better solution is to factor out the complex calculation into a separate subroutine, then call that subroutine within a now much simpler and more readable map expression: my @jittered_points = map { jitter($_) } @points; |