Previous Page
Next Page

6.13. Complex Mappings

Use a subroutine call to factor out complex list transformations.

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:

[*] map blocks rarely start out complex, but the complexity of any critical piece of code will tend to grow over time. This fall from grace seems to be caused not so much by the Second Law of Thermodynamics as by the First Law of Murphy.

    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;

    
# and elsewhere...

    # Add a random Gaussian perturbation to a point...
sub jitter { my ($point) = @_; my $x = $point->{x}; my $y = $point->{y}; my $max_jitter = max($x, $y) / $JITTER_FACTOR; return { x => $x + gaussian_rand({ mean=>0, dev=>0.25, scale=>$max_jitter }), y => $y + gaussian_rand({ mean=>0, dev=>0.25, scale=>$max_jitter }), }; }

    Previous Page
    Next Page