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

Ahoy, ye salty Monks.

I just had a situation where I wanted to perform a transformation on every instance of a pattern in a string, but that transformation is dynamic, and I don't know how to do that.

For example, say I want to replace every instance of a number with a different form of the same number. Specifically, I want to replace 20000 with 20 K, 20000000 with 20 M, and 12345678 with 12 M.

The first thing I tried was:

sub munge { my $num = shift; if ( $number > 1000000 ) { return ($number / 1000000).' M'; } if ( $number > 1000 ) { return ($number / 1000).' K'; } } my $text = <<END; Hello 200000 how 12345678 are you? I have 35000 dogs. I consume 32000000 hamburgers a day. END $text =~ s/(\d+)/munge($1)/g;

...but that didn't work.

Thanks!
--Pileofrogs

Replies are listed 'Best First'.
Re: substitute with sub result?
by toolic (Bishop) on Mar 07, 2009 at 01:00 UTC
    You need the e modifier:
    $text =~ s/(\d+)/munge($1)/eg; ^ |

    Also, it should be:

    my $number = shift;

    Update: Or, if you prefer engineering notation, you could try the "eng_not" sub below instead of your "munge". It's just something I have lying around:

    sub eng_not { # Format a numeric value using engineering notation. # Examples: # 1234 --> 1.23k # 5e-5 --> 50u my $num = shift; my $mult; my $prefix; my $val; my $sign = ($num<0) ? '-' : ''; $num = abs $num; if ( ($num > 1e15) || ($num < 1e-15) ) { return sprintf '%3.2e', $n +um } if ($num > 1e12) { $prefix = 'T'; $mult = 1e12; } elsif ($num > 1e9) { $prefix = 'G'; $mult = 1e9; } elsif ($num > 1e6) { $prefix = 'M'; $mult = 1e6; } elsif ($num > 1e3) { $prefix = 'k'; $mult = 1e3; } elsif ($num > 1e0) { $prefix = '' ; $mult = 1e0; } elsif ($num > 1e-3) { $prefix = 'm'; $mult = 1e-3; } elsif ($num > 1e-6) { $prefix = 'u'; $mult = 1e-6; } elsif ($num > 1e-9) { $prefix = 'n'; $mult = 1e-9; } elsif ($num > 1e-12) { $prefix = 'p'; $mult = 1e-12; } $val = sprintf '%f', $num/$mult; $val = substr($val,0,4); # only want 3 digits and decimal point $val =~ s/0$//; # delete 1st trailing 0, if any $val =~ s/0$//; # delete 2nd trailing 0, if any $val =~ s/\.$//; # delete trailing decimal point, if any return $sign . $val . $prefix; }

      Woah! Cool!

      Where is that documented? I don't see it in perlre or perlop.

      D'oh... oh yes it is...right there in perlop...

      Carry on.. nothing to see here...

Re: substitute with sub result?
by almut (Canon) on Mar 07, 2009 at 01:02 UTC

    For one, you need s/.../.../eg.  The e makes the substitution part be treated as Perl code.

    Then, it should also be my $number = shift;  (use strict; would've told you... :)

Re: substitute with sub result?
by CountZero (Bishop) on Mar 07, 2009 at 09:09 UTC
    Have a look at Format::Number Number::Format (thanks Toolic and FunkyMonk for pointing out this mistake.)

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James