Приглашаем посетить
Цветаева (tsvetaeva.lit-info.ru)

Section A.16.  Answer for Chapter 17

Previous
Table of Contents
Next

A.16. Answer for Chapter 17

A.16.1. Exercise

Let's start with the test file. We write this first, and as we write the code (which we can't run until we write the module), we get an idea of the interface that we want.

In the BEGIN block, we first test if we can use My::List::Util. This will obviously fail, since we haven't written the module yet. We'll worry about that later.

Next, we check if we've defined the sum subroutine. Once we implement the My::List::Util minimal module, the use_ok will pass but this will fail. That's Test Driven Development. You define what you want, ensure that the test fails when the infrastructure is missing, then make it pass. Getting tests to pass can be easy if we don't care if they fail when they should.

After we check for the sum routine, we test a series of sums, using different numbers of arguments and different values. We don't have to identify every special case at this point (we can always add tests later), but we want to ensure that we test several different ways to call the subroutine. We even throw in a test where we pass it a non-number and a number, then a test where we pass it two non-numbers.

We do something different for shuffle. We ensure the subroutine is defined, then define $array as a starting point, but immediately copy it to $shuffled so we don't disturb the original. Before we've written the code, we've decided to pass an array reference, and that will allow our routine to affect the data that we pass to it (rather than creating a copy).

To test this, we do something really simple. We compare the original array to the shuffled one and use cmp_ok to test that at least two positions are different. That wouldn't be a very good shuffle, but we're going to punt to you for the rest of the tests.

BEGIN{ use_ok( 'My::List::Util' ) }

use Test::More 'no_plan';

# # # # # # sum
ok( defined &sum, 'The sum(  ) routine exists');
is( sum( 2, 2    ), 4, '2 + 2 = 4'     );
is( sum( 2, 2, 3 ), 7, '2 + 2 + 3 = 7' );
is( sum( ),         0, 'null sum is 0' );
is( sum( -1 ),     -1, '-1 = -1'       );
is( sum( -1, 1 ),   0, '-1 + 1 = 0'    );
is( sum( 'Ginger', 5 ),
        5, 'A string + 5 = 5' );
is( sum( qw(Ginger Mary-Ann) ),
        0, 'Two strings give 0' );

# # # # # # shuffle
ok( defined &shuffle, "The shuffle(  ) routine exists");
my $array = [qw( a b c d e f )];

my $shuffled = $array;
shuffle( $shuffled );

my $same_count = 0;

foreach my $index ( 0 .. $#$array ) {
        $same_count++ if $shuffle->[$index] eq $array->[$index];
        }

cmp_ok( $same_count, '<', $#$array - 2,
        'At least two positions are different');

Now that we have the tests, we write the code. As we write the code, we run the tests. At first, most of the tests will fail, but as we add more code (and possibly debug the code that's already there), more and more tests pass. Here's our finished module:

package My::List::Util;
use strict;

use base qw(Exporter);
use vars qw(@EXPORT $VERSION);

use Exporter;

$VERSION = '0.10';
@EXPORT  = qw(sum shuffle);

sub shuffle {          # Fisher-Yates shuffle from perlfaq4
        my $deck = shift;  # $deck is a reference to an array
        my $i = @$deck;
        while ($i--) {
                my $j = int rand ($i+1);
                @$deck[$i,$j] = @$deck[$j,$i];
                }
        }

sub sum {
        my @array = @_;

        my $sum = 0;

        foreach my $element ( @array ) {
                $sum += $element;
                }

        $sum;
        }

1;


Previous
Table of Contents
Next