Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

cool way of manipulating array elements

by pashanoid (Scribe)
on Aug 30, 2011 at 07:42 UTC ( [id://923136]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Bretheren, I'm getting somewhat lazy. Is there a "cool" way of multiplying all elements of an array by say "2" without doing a foreach/for/while thing?

my @array = ('1','2','45','65','what is this?'); #want something like: my @array*2 = ('2','4','90','130','what is this?');

Is this possible? multiply all values, skip non-numeral ones?

Replies are listed 'Best First'.
Re: cool way of manipulating array elements
by BioLion (Curate) on Aug 30, 2011 at 08:02 UTC

    Maybe map? and a util function to test for whether each element is a number - Scalar::Util?

    use strict; use warnings; # or some other util function that can tell if it is a number... use Scalar::Util qw(looks_like_number); my @array = ('1','2','45','65','what is this?'); my @mod = map {if (looks_like_number($_)){$_*2} else {$_}} @array; print "$_\n" for @mod;

    Hope this helps... (I am a bit Rusty, so be gentle...)

    Just a something something...
      thank you! works great!

        If you want to modify the original array contents then you could modify BioLion's code thus:

        $_ *= 2 for grep {looks_like_number($_)} @array;
        True laziness is hard work
Re: cool way of manipulating array elements
by JavaFan (Canon) on Aug 30, 2011 at 09:20 UTC
    foreach/for/while is uncool? Anyway, there's always the trusty goto (adjust regexp to taste):
    my $i = 0; again: $array[$i] *= 2 unless $array[$i] =~ /[^0-9]/; goto again if ++$i <= $#array;
    Or as a bare block:
    my $i = 0; {$array[$i] *= 2 unless $array[$i] =~ /[^0-9]/; redo if ++$i <= $#array;}
    But I prefer a for:
    /[^0-9]/ or $_ *= 2 for @array;
    Why would that not be cool enough?
      /[^0-9]/ or $_ *= 2 for @array;

      Mind blown.

Re: cool way of manipulating array elements
by Perlbotics (Archbishop) on Aug 30, 2011 at 10:21 UTC

    Why? I suspect, you want to save typing and maybe gain readability - then use a sub.

    Getting it done without a loop - explicit (foreach, while, etc.) or implicit (map, grep, etc.) is something you cannot get around - unless manually unrolling the loop, but that isn't flexible.

    Three more ways to obscure, your're using a loop:

    use strict; use warnings; use Scalar::Util qw(looks_like_number); #-- Common helper sub mul_if_num { my ($expr, $factor) = @_; return looks_like_number( $expr ) ? $factor * $expr : $expr; } #-- Cool1: Overloading { package ArrayMul; use overload '*=' => \&mulby; sub new { my $self = shift; return bless [ @_ ], __PACKAGE__; } sub mulby { my ($self, $factor, $swap) = @_; @$self = map{ main::mul_if_num( $_, $factor ) } @$self; return $self; } 1; } package main; #-- Cool2: hide loop in sub - profit from re-use and readability sub scalar_mul { my ($aref, $factor) = @_; #TODO: parameter checks (2 params, first is ref, second is scalar) $_ = mul_if_num( $_, $factor ) for @{$aref}; die 'ill. context' if defined wantarray; } #-- Examples my @array = qw(1 2 3 4 what? +1 -7 666+666); my $array2 = ArrayMul->new( @array ); my @array3 = @array; my @array4 = @array; #-- Cool1! but no @-sigil. Unexpected side-effects likely. $array2 *= 2; #-- Cool2! loop hidden elsewhere scalar_mul( \@array3, 3 ); #-- Cool3! but creates new array use List::MoreUtils qw(apply); @array4 = apply { $_ = mul_if_num( $_, 4 ) } @array; #-- Results print "@{$_}\n" for ( \@array, $array2 ,\@array3 , \@array4); __END__ 1 2 3 4 what? +1 -7 666+666 2 4 6 8 what? 2 -14 666+666 3 6 9 12 what? 3 -21 666+666 4 8 12 16 what? 4 -28 666+666

    Personally, I would prefer the ... scalar_mul( \@array3, 3 ); ... variant.

Re: cool way of manipulating array elements
by duyet (Friar) on Aug 30, 2011 at 08:11 UTC
    print map {( $_ =~ /\d/ ? $_ * 2 : '-' ),"\n"} @array;

    Maybe there is other way to do this but this is one ...

      Be careful with such a "lazy" regexp, it might give you funny results (looks_like_number is smarter ;):

      @array = ('abc2', 'hehe42'); # not really numbers, but passes regexp ; +)
      print map {( $_ =~ /^-?\d+$/ ? $_ * 2 : '-' ),"\n"} @array;

      You could try something like this to catch integers.

      print map {( $_ =~ /^-?(\d*\.)?\d+$/ ? $_ * 2 : '-' ),"\n"} @array;

      Or something like this to catch numbers with decimals. Both of these will simply ignore leading zeros as perl is apt to do when converting strings to numbers.

Re: cool way of manipulating array elements
by davido (Cardinal) on Aug 30, 2011 at 09:25 UTC

    One solution is to grep to find the indices of the elements you wish to multiply, and then feed those indices to map for it to act upon. Here is a function that takes an array ref as the first parameter, and a multiplier as the second parameter. The return value is an array reference to a new array containing the modified values, only for those elements in the original array that "look like numbers."

    use strict; use warnings; use Scalar::Util qw{ looks_like_number }; use v5.12; my @array = ( '1', '2', '45', '65', 'what is this' ); my @by2 = @{ multiply( \@array, 2 ) }; say join ', ', @by2; sub multiply { my( $aref, $multiplier ) = @_; return [ map { $aref->[$_] * $multiplier } grep { looks_like_number( $aref->[$_] ) } 0 .. $#{$aref} ]; }

    Dave

Re: cool way of manipulating array elements
by shawnhcorey (Friar) on Aug 30, 2011 at 14:57 UTC

    Do be clever. Write your code so that it is understandable. Use a foreach loop.

      I wish I could upvote this multiple times. map is very cool, and can give you a marvelously compact syntax, but a simple foreach or while loop does the same thing and is (usually) easier to read.

        I use map, not for its "compact syntax", but depending on the use case. This example, in my opinion, is the perfect use case for map: transform an array using some criteria and return a new array.

        for (or, foreach if you like) has its place, but I don't find it inherently more readable than map.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://923136]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2024-04-19 23:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found