http://www.perlmonks.org?node_id=237686

in reply to Re^2: Fold a list using map splices
in thread Fold a list using map splices

Nice twist++. Took me a while to work out why i doesn't destroy the input array. Not sure that I share your distaste for the index variable...'sides...mine's quicker:).

You have a typo? ... an out-by-one error on your version of mapnz, it produces one too many rows. You either need to subtract 1 as you do in the mapn version or start your range from 1.

Examine what is said, not who speaks.
1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
3) Any sufficiently advanced technology is indistinguishable from magic.
Arthur C. Clarke.
• Comment on Re: Re^2: Fold a list using map splices

Replies are listed 'Best First'.
Re^4: Fold a list using map splices
by Aristotle (Chancellor) on Feb 22, 2003 at 15:24 UTC

I was pretty sure yours would be quicker - but by how much is that?

Thanks for the heads up on the one-off error..

Makeshifts last the longest.

You're both inspirational, guys++. However, it seems to me that you ought to put the padding at the end of the array; with the exception being that if the number of columns divides the length of the array with no remainder then you don't add the padding.

Also I think that the splice on each iteration is wasteful, so I'll just change the indexes so that each row comes out correct.

```sub map_nbsp (&@) {
my (\$codeblock, \$cols) = splice @_, 0, 2;
splice @_, @_, 0, ('&nbsp;')x\$cols if @_ % \$cols;
my \$rows = int @_/\$cols;
map{
\$codeblock->(@_[\$cols*\$_ .. \$cols*(\$_+1)-1]);
} 0 .. (\$rows)-1;
}

my @tabular_data = map{ 1000+int rand 8999 } 1 .. 9;

print
table( {border=>1},
map_nbsp{ Tr( td( \@_ ) ); } 4, @tabular_data
);

I'm not sure why I used splice where I could have used unshift. Probably for the same reason you used it instead of push — too fixated on splicing. :-)

You misunderstood the purpose of the padding in my code, btw. Remember that I splice before invoking the callback — which means that without padding, I'd be throwing away the values that should have been processed in the first iteration. I'm unsure what kind of pretzel logic led me to that solution, I should simply have used the list returned from splice.

```sub mapn (&@) {
my ( \$callback, \$n ) = splice @_, 0, 2;
map \$callback->( splice @_, 0, \$n ), 0 .. @_ / \$n -1;
}

One thing about your solution that irks me since my days as an assembler programmer is multiplying a constant with a counter in a loop. You get the same effect if you simply iteratively add the constant to a counter. Of course, it doesn't make a particular difference in efficiency in Perl, but I find that it can still improve clarity and reduce repetition to do it that way.

```sub mapnz (&@) {
my ( \$callback, \$n ) = splice @_, 0, 2;
my ( \$i, \$j ) = ( 0 ) x 2;
map {
\$i = \$j;
\$callback->( @_[ \$i .. ( \$j += \$n ) - 1 ] );
} 0 .. @_ / \$n - 1;
}

As well, the non-padding case can be dealth with less cumbersomely than my and BrowserUk's previous attempts do, which rely on that really ugly ternary.

The splicing flavour (see 2nd update):

```sub mapn (&@) {
my ( \$callback, \$n ) = splice @_, 0, 2;
map {
\$n = @_ if \$n > @_;
\$callback->( splice @_, 0, \$n );
} 0 .. @_ / \$n - 1;
}

Analogously, the index-based flavour:

```sub mapn (&@) {
my ( \$callback, \$n ) = splice @_, 0, 2;
my ( \$i, \$j ) = ( 0 ) x 2;
map {
\$i = \$j;
\$n = @_ - \$i if \$i + \$n > @_;
\$callback->( @_[ \$i .. ( \$j += \$n ) - 1 ] );
} 0 .. @_ / \$n - 1;
}

I think I'd still prefer the splicing version.

Update: the index-based flavour had \$n = \$#_ - \$i if \$i + \$n > \$#_; which is an off-by-one error, since the range goes to ( \$j += \$n ) - 1. It has indeed got to be completely analogous to the splicing flavour, using @_ instead. Fixed.

Update: I am amused at myself. The "padding" splicing version actually didn't pad, because splice, of course, only returns as many elements as there are left, if you ask for more. The padding version turns out to be much simpler than the "non-padding" version I concocted before — no special cases at all!

```sub mapnz (&@) {
my ( \$callback, \$n ) = splice @_, 0, 2;
push @_, ( undef ) x ( -@_ % \$n );
map \$callback->( splice @_, 0, \$n ), 0 .. @_ / \$n - 1;
}