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

I need to grab the last number in an integer produced from a loop. Right now, my loop is only going to 300. I want to grab just the right most digit in the integer. My current code:

perl -e 'for ( $dec = 0 ; $dec <= 300 ; $dec++ ) { $one =~ /\d+${1}$/; +print "$dec -> $one\n"; }'

Desired Output

1 -> 1
2 -> 2
3 -> 3
.
.
.
45 -> 5
46 -> 6
47 -> 7
.
.
.
137 -> 7
138 -> 8
139 -> 9
140 -> 0
141 -> 1

etc etc
Special request
I appreciate all assistance I get from the monkage. However, if there is no way to get my desired output using an RE or that an RE is just plain not worth it I request you just let me know this without attempting to give me working code.
This will allow me to find an answer and I would like to find a different means on my own first so I can learn this language!! :) Right now, my wish is to do it with a working RE.

----------
- Jim

Replies are listed 'Best First'.
Re: RE question...yup, another one ;)
by btrott (Parson) on May 26, 2001 at 23:26 UTC
    Okay... well, since you don't want a direct answer, I will give you an answer in a black box that you don't have to read, and then I will give you a clue.

    A regex is *way* too big a tool for this job.

    print $_ % 10, "\n" for 0..300;

    Here's your hint: think of a mathematical operator that might do what you want here. It's in perlop, under Multiplicative Operators.

      For the solution above, btrott, I believe you have it wrong. What happens when you get to 300? It prints 3000. Not the desired result; but I think I see where you are going with this. . . so here goes a quick attempt.
      print $_ . "-->" . $_ % 10 for 0..300
        It doesn't print 3000 when I run the program. Your solution that you posted looks exactly the same as mine, except you print out the $_ and the '-->'. What is the difference?

        What version of perl are you running where it prints 3000?

        3000? doh!

        "Argument is futile - you will be ignorralated!"

japhy regex analysis: case study (RE question...)
by japhy (Canon) on May 27, 2001 at 08:41 UTC
    Let me point out a couple notes on using regexes to solve this problem; first, though, using modulus is definitely the way to go (it averages between 3 and 2.5 times faster than regex approaches). It is important, when given a language such as Perl, to know which tools are hammers, screwdrivers, and wrenches; and even moreso, which problems are nails, screws, and bolts. This is a case where you are using a sledgehammer (a regex) to flatten a piece of wood, when a simple piece of sandpaper (the % operator) will do.

    There are four generic approaches to this:

    /(\d)*(\d)/
    useless capturing of preceeding digits; actually captures one digit at a time over and over again; useless backtracking is forced; 2.96 x slower than modulus

    /(\d*)(\d)/
    useless capturing of preceeding digits; useless backtracking is forced; 2.80 x slower than modulus

    /\d*(\d)/
    useless backtracking is forced; 2.79 x slower than modulus

    /(\d)$/
    optimized (goes to end of string automatically); 2.54 x slower than modulus
    Code used for benchmarks:
    use Benchmark 'timethese'; $x = int (1_000_000 * rand 1_000_000); timethese(-5, { multiple => sub { $x =~ /(\d)*(\d)/ }, backtrack_c => sub { $x =~ /(\d*)(\d)/ }, backtrack => sub { $x =~ /\d*(\d)/ }, opt => sub { $x =~ /(\d)$/ }, mod => sub { $x % 10 }, });
    If snafu doesn't mind, I'd like to use this as an example in my book -- first to show how to craft a regex, then to show why there are some places where a regex is overkill.

    japhy -- Perl and Regex Hacker

      Well, since you resorted to benchmarks (updated)...

      Rate bt_c mult bt opt mod chop bt_c 180870/s -- -1% -5% -28% -62% -74% mult 181987/s 1% -- -4% -27% -62% -74% bt 189426/s 5% 4% -- -24% -60% -73% opt 249612/s 38% 37% 32% -- -48% -64% mod 476214/s 163% 162% 151% 91% -- -31% chop 692944/s 283% 281% 266% 178% 46% --
      So chop is over 45% faster than mod even though it had to make an extra copy of the string!
      use Benchmark 'cmpthese'; $x = int (1_000_000 * rand 1_000_000); cmpthese( -3, { mult => sub { $x =~ /(\d)*(\d)/ }, bt_c => sub { $x =~ /(\d*)(\d)/ }, bt => sub { $x =~ /\d*(\d)/ }, opt => sub { $x =~ /(\d)$/ }, mod => sub { $x % 10 }, chop => sub { my $x= $x; chop $x }, });


      Following are the original bogus results. Thanks to dkubb for mentioning my over local. I realized I'd made a mistake and came back but not quick enough. So it looks like local is quite a bit slower than my (which makes sense), so I'd be interested in how japhy's machine compares the new code.

      Rate mult bt_c bt opt mod chop mult 180759/s -- -1% -7% -31% -62% -71% bt_c 182581/s 1% -- -6% -31% -62% -70% bt 193680/s 7% 6% -- -26% -60% -69% opt 263234/s 46% 44% 36% -- -45% -57% mod 481067/s 166% 163% 148% 83% -- -22% chop 618559/s 242% 239% 219% 135% 29% --
      So chop is almost 30% faster than mod even though it had to make an extra copy of the string!
      use Benchmark 'cmpthese'; $x = int (1_000_000 * rand 1_000_000); cmpthese( -3, { mult => sub { $x =~ /(\d)*(\d)/ }, bt_c => sub { $x =~ /(\d*)(\d)/ }, bt => sub { $x =~ /\d*(\d)/ }, opt => sub { $x =~ /(\d)$/ }, mod => sub { $x % 10 }, chop => sub { local $x; chop $x }, });

              - tye (but my friends call me "Tye")
        Even on a different machine, I get these results (the machine has the 5.005 version of Benchmark.pm):
        timethese( -3, { mod => sub { $x % 10 }, chop => sub { chop(my $x = $x) }, substr => sub { substr($x, -1) }, }); __END__ (they ran for at least 3 seconds) chop: 8707.14/s (n= 29256) substr: 43620.45/s (n=136532) mod: 48906.87/s (n=163838)
        So on my machine, mod is much faster than chop; the unexplored substr approach is nearly as fast.

        japhy -- Perl and Regex Hacker
        Except that you never actually stored anything in local $x.
        use Benchmark 'cmpthese'; $x = int (1_000_000 * rand 1_000_000); cmpthese( -3, { mod => sub { $x % 10 }, chop => sub { local $x; chop $x }, chop2 => sub { chop(local $x = $x) }, }); __END__ Rate chop2 chop mod chop2 25430/s -- -88% -93% chop 204396/s 704% -- -41% mod 348051/s 1269% 70% --
        On my machine, chop() was slower. But the real chop() approach was slower still.

        japhy -- Perl and Regex Hacker
      Japhy, Sure!! I would love it to be in your book. And I appreciate the thorough answer. I admit, the one thing I never even considered was the analogous nuts and bolts, hammers and wrenches of Perl.

      Thanks!

      ----------
      - Jim

Re: RE question...yup, another one ;)
by nardo (Friar) on May 26, 2001 at 23:23 UTC
    perl -e 'for ( $dec = 0 ; $dec <= 300 ; $dec++ ) {$dec =~ /(\d)$/ && print "$dec -> $1\n"; }'
      Wow, that was fast. I was actually about to say that I figured it out. But now, I notice my solution is a bit different from yours nardo. Which is better?

      perl -e 'for ( $dec = 0 ; $dec <= 300 ; $dec++ ) { ($one = $dec) =~ s +/(\d)+(\d)/$2/;print "$dec -> $one\n"; }'

      ----------
      - Jim

        Which is better I suppose depends on personal style. Personally, I think that
        perl -e 'for (0..300) {print "$_ -> ", chop, "\n"; }'
        is best, but the best approach using a regex is, in my opinion,
        perl -e 'for (0..300) {/(\d)$/ && print "$_ -> $1\n"; }'
        I think it's better style to use parenthesis inside a regex to match data and put it in $1 than it is to assign the whole thing to a new variable and then remove everything except what you want.