For Programmers: Free Programming Magazines  


Home > Archive > PERL Miscellaneous > March 2004 > New module: Array::Each









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author New module: Array::Each
Brad Baxter

2004-03-27, 11:57 pm

Note: this is sort of a continuation of the thread,
Proposal: new module, Array::Each?
http://groups.google.com/groups?thr...er.libs.uga.edu

I've uploaded version 0.02 of Array::Each to CPAN,
http://www.cpan.org/authors/id/B/BB/BBAXTER/

It's also available from my web site, where you can read a bit more of the
docs without unpacking anything,
http://www.vitabrevis.com/perl/modules/Array-Each

This is my first CPAN contribution, and it has been a bit of a
professional development project. I wanted to study and use best
practices as regards module design, coding, documentation, testing, and
packaging. In the process, my appreciation for maintaining test suites
has *skyrocketed*.

I would still appreciate discussion, criticism, or any other feedback
about the module.

For starters, the following line of code returns an element from each
array in $set (plus $c):

return ( (map $_->[$i], @$set), $c )

And this code returns groups of elements from each array, and it returns
the value in $undef if we've gone past the end of any array:

my @ret;
foreach my $aref ( @$set ) {
push @ret, map {$_<@$aref ? $aref->[$_] : $undef}
($i..$i+$group-1) }
return ( @ret, $c );

I'd like to think this could be done with two maps in succession, but
inspiration just hasn't come.

Thanks,

Brad
Anno Siegel

2004-03-28, 10:22 pm

Brad Baxter <bmb@ginger.libs.uga.edu> wrote in comp.lang.perl.misc:

> I've uploaded version 0.02 of Array::Each to CPAN,
> http://www.cpan.org/authors/id/B/BB/BBAXTER/
>
> It's also available from my web site, where you can read a bit more of the
> docs without unpacking anything,
> http://www.vitabrevis.com/perl/modules/Array-Each
>
> This is my first CPAN contribution, and it has been a bit of a


Congrats :)

[...]

> And this code returns groups of elements from each array, and it returns
> the value in $undef if we've gone past the end of any array:
>
> my @ret;
> foreach my $aref ( @$set ) {
> push @ret, map {$_<@$aref ? $aref->[$_] : $undef}

^
What is that "$"? Not the result of a copy/paste operation for sure :)

> ($i..$i+$group-1) }
> return ( @ret, $c );
>
> I'd like to think this could be done with two maps in succession, but
> inspiration just hasn't come.


The inner map can be written as a slice: @$aref[ $i .. $i + $group - 1]"
That will give you undef beyond the array limits. Since $_ is no longer
used in the inner expression, now the outer loop can become a map:

my @ret = map @$_[ $i .. $i + $group - 1], @$set;
return ( @ret, $c);

Anno
Brad Baxter

2004-03-29, 12:33 pm

On Sun, 28 Mar 2004, Anno Siegel wrote:

> Brad Baxter <bmb@ginger.libs.uga.edu> wrote in comp.lang.perl.misc:
>
> [...]
>
> ^
> What is that "$"? Not the result of a copy/paste operation for sure :)
>

In fact, it is a copy/paste, but I can see it's confusing out of context.
Here's another example of that construct that perhaps shows better my
intent:

sub each_group { # group is defined
my $self = shift;
my $group = $self->[GROUP];
my $i = $self->[ITERATOR]; # inlined
$self->[ITERATOR] += $group; # incr_iterator
$self->[ITERATOR] = $self->[REWIND], # inlined rewind
return if grep {$i >= @$_} @{$self->[SET]};
my @ret;
foreach my $aref ( @{$self->[SET]} ) {
push @ret,
map {$_<@$aref ? $aref->[$_] : $self->[UNDEF]}
($i..$i+$group-1) }
( @ret, $i );
}

So you see, $undef is really $self->[UNDEF], a user-definable value to be
used when the code falls off the end of an array.
[color=darkred]
>
> The inner map can be written as a slice: @$aref[ $i .. $i + $group - 1]"
> That will give you undef beyond the array limits. Since $_ is no longer
> used in the inner expression, now the outer loop can become a map:
>
> my @ret = map @$_[ $i .. $i + $group - 1], @$set;
> return ( @ret, $c);


Yes, this will give me undef beyond the array limits, but I want it to
give me $self->[UNDEF] (but not change _existing_ undefined elements).

I took another close look at perlvar to see if I missed a special
variable. I looked for one that would let me tell perl what to supply in
the case of a slice element (or array element autovivification, too?) when
it goes past the end. Something along these lines:

my ( @a, @b );
{
local ${^UNDEF} = ''; # imaginary special variable
@a = (undef)[0..2]; # note, undef NOT changed when explicit
$#b = 2;
}

Then @a would contain (undef,'','') and @b would contain ('','','')
instead of the current (undef,undef,undef). I don't think such a special
variable exists--at least, I didn't see reference to one.

Regards,

Brad
Anno Siegel

2004-03-29, 1:51 pm

Xref: kermit comp.lang.perl.misc:311321 comp.lang.perl.modules:43575

Brad Baxter <bmb@ginger.libs.uga.edu> wrote in comp.lang.perl.misc:
> On Sun, 28 Mar 2004, Anno Siegel wrote:
>
>
> In fact, it is a copy/paste, but I can see it's confusing out of context.
> Here's another example of that construct that perhaps shows better my
> intent:
>
> sub each_group { # group is defined
> my $self = shift;
> my $group = $self->[GROUP];
> my $i = $self->[ITERATOR]; # inlined
> $self->[ITERATOR] += $group; # incr_iterator
> $self->[ITERATOR] = $self->[REWIND], # inlined rewind
> return if grep {$i >= @$_} @{$self->[SET]};
> my @ret;
> foreach my $aref ( @{$self->[SET]} ) {
> push @ret,
> map {$_<@$aref ? $aref->[$_] : $self->[UNDEF]}
> ($i..$i+$group-1) }
> ( @ret, $i );
> }
>
> So you see, $undef is really $self->[UNDEF], a user-definable value to be
> used when the code falls off the end of an array.


Well... the wisdom in the choice of variable name is doubtful, especially
since a spurious "$" in front of something else is a popular typo.
"my $self = $shift" anyone?

>
> Yes, this will give me undef beyond the array limits, but I want it to
> give me $self->[UNDEF] (but not change _existing_ undefined elements).


You could add a supply of the local undef's past the end of the list:
(untested)

my @ret = ( @$_, ( $undefined_value ) x $group)[ $i .. $i + $group - 1];

But let me say that a guts feeling tells me these individual undefs are a
misfeature. They are presumably there to tell the user that the algorithm
had to pull elements from past the end of one or more arrays. In many cases
the user won't care and be quite happy with standard undef's. If they do
care, they can bloody well fill their own arrays with distinctive values :)
It could be that the feature encumbers your algorithm more than it's worth.

> I took another close look at perlvar to see if I missed a special
> variable. I looked for one that would let me tell perl what to supply in
> the case of a slice element (or array element autovivification, too?) when
> it goes past the end. Something along these lines:


No such thing.

Anno
Brad Baxter

2004-03-29, 4:34 pm

On Mon, 29 Mar 2004, Anno Siegel wrote:

> Brad Baxter <bmb@ginger.libs.uga.edu> wrote in comp.lang.perl.misc:


>
> Well... the wisdom in the choice of variable name is doubtful, especially
> since a spurious "$" in front of something else is a popular typo.
> "my $self = $shift" anyone?


Point well taken.

> [...]
> You could add a supply of the local undef's past the end of the list:
> (untested)
>
> my @ret = ( @$_, ( $undefined_value ) x $group)[ $i .. $i + $group - 1];


Ah! That's the inspiration I was hoping for. And unless I'm doing
something wrong, it benchmarks nearly twice as fast:

use Benchmark 'cmpthese';

my $count = shift||100000;

my $undef_val = '_';
my $group = 30;
my $set = [['a'..'z'], [1..26]];
my $i = 0;

cmpthese($count, {
'foreach' => sub {
my @ret;
foreach my $aref ( @$set ) {
push @ret, map {$_<@$aref ? $aref->[$_] : $undef_val}
($i..$i+$group-1) }

},
'map_slice' => sub {
my @ret = map { (@$_,($undef_val)x$group)[$i..$i+$group-1] }
@$set;
},
});

__END__
Benchmark: timing 100000 iterations of foreach, map_slice...
foreach: 28 wallclock secs (26.35 usr + 0.00 sys = 26.35 CPU) @
3795.07/s (n=100000)
map_slice: 15 wallclock secs (14.02 usr + 0.00 sys = 14.02 CPU) @
7132.67/s (n=100000)
Rate foreach map_slice
foreach 3795/s -- -47%
map_slice 7133/s 88% --



>
> But let me say that a guts feeling tells me these individual undefs are a
> misfeature. They are presumably there to tell the user that the algorithm
> had to pull elements from past the end of one or more arrays. In many cases
> the user won't care and be quite happy with standard undef's. If they do
> care, they can bloody well fill their own arrays with distinctive values :)
> It could be that the feature encumbers your algorithm more than it's worth.
>


Well, the feature is mostly avoided anyway, and the value *is* undef by
default. I like the idea because it gives an option that doesn't force
anyone to alter (or copy) the arrays they're iterating over.


>
> No such thing.


As I suspected. :-)

Many thanks, as always.

Brad
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com