weingart has asked for the wisdom of the Perl Monks concerning the following question:

Ok, so I thought I knew perl pretty well. I sling it on a daily basis. This one has me stumped. Anybody know of a faster way to do this?
# Change 'from' to 'to' throughout 2D-array sub propogate { my($arry) = shift; my($from) = shift; my($to) = shift; my($row); map { map { $_ = $to if($_ == $from); } @{$_}; } @{$arry}; };
--Toby.

Replies are listed 'Best First'.
Re: Fast search/replace?
by chipmunk (Parson) on Feb 14, 2001 at 01:11 UTC
    This probably isn't much faster, but as a personal preference I avoid using map in void context:
    sub propogate { my($array, $from, $to) = @_; foreach (@$array) { foreach (@$_) { $_ = $to if $_ == $from; } } }

      Perhaps this isn't a void context, since the output of the map is being returned from the sub? It would be odd to return an array given that you passed an array ref, but ... I suppose you could wrap the maps in [] and fix that up.

      Philosophy can be made out of anything. Or less -- Jerry A. Fodor

        I modify the original array (have to). Don't care if I pass it back, it would be a void context.
      This is a *little* faster. Why? But it's still not fast enough. I'm working on an image (320x240), and this is not pretty (5+ minutes for each test). Maybe I should look into using a 1D array?
      --Toby.
        If you're performing matrix operations, the PDL module may come in handy. Otherwise, working with images, you may have more luck with Image::Magick.

        Either approach will use C code, which is likely many times faster for this operation.

        Constructing an image with list values representing individual pixels is a bit like making a cake by assembling crumbs.

        Even a 1-dimensional array will not provide any acceptable level of performance for this task. There's just alot of overhead involved in processing list values, as compared with a raw buffer.

        If none of the suggested image modules do what you are looking for, I would first recommend that you store and modify your image in place, as a raw scalar stream of bytes. If you're simply trying to swap byte A with byte B, applying the tr/// operator will probably suffice, as it uses an efficient table-lookup technique. If you need to swap 16-bit or larger words, the s/// operator may also offer an acceptable level of performance, but won't come close to what you could do with optimized assembler or C code.

        Manipulating raw buffers in an efficient manner is not one of Perl's strengths :|

           MeowChow                                   
                       s aamecha.s a..a\u$&owag.print

        If you must do it in Perl, maybe. But if you're processing an image, maybe you want to look into Image::Magick. Or maybe you want to write this routine in C / XS?

        Philosophy can be made out of anything. Or less -- Jerry A. Fodor

        Maybe think about storing the data in a single string and working it with vec. Vec can be sexy fast on certain tasks. OTOH, Perl isn't your friend when it comes to too much bit-twiddling. You likely should take the other's advice and look at Image::Magick or PDL. You'll be happier in the end if you can job image creation out to C routines.

        --
        $you = new YOU;
        honk() if $you->love(perl)

Re: Fast search/replace?
by Anonymous Monk on Feb 14, 2001 at 04:40 UTC
    Maybe a regex substitute would be faster, if you know the delimiter won't appear in the array. Something like:
    foreach (@$arry) { my $x = join("\n", @$_); $x =~ s/^${from}$/${to}/mg; @$_ = split("\n", $x); }
    Also, if you're just modifying the array in-place, map is inefficient because it accumaltes the results into an array.