Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

First char out of a string & surprises with errors on chop reverse $string (that doesn't work!)

by eduardo (Curate)
on Dec 26, 2001 at 21:41 UTC ( [id://134428]=perlquestion: print w/replies, xml ) Need Help??

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

Greetings fellow monks. So, earlier today in the chatterbox, someone said: "How do I get the first character from a string? I know I can reverse and chop, but there has to be an easier way." Well, this got me started thinking about the different ways that one could try to get the first character out of a string. So, I enumerated the total set I could think of, benchmarked them... and ran into a suprise that has surprised me, and baffled jeffa as well! So, let's start off with my first stab at the code, shall we?
#!/usr/bin/perl -w use strict; use Benchmark; my $string = "a man, a plan, a canal, panama!"; sub using_regexp { $string =~ /^(.)/o; return $1; } sub using_split { return (split('', $string, 2))[0]; } sub using_substring { return substr($string, 0, 1); } sub using_unpack { return unpack('A1', $string); } sub using_reverse_chop { return chop reverse $string; } timethese(-5, {'Using Regexp' => \&using_regexp, 'Using Split' => \&using_split, 'Using Substring' => \&using_substring, 'Using Unpack' => \&using_unpack, 'Using Reverse & Chop' => \&using_reverse_chop, });
Seems reasonable enough, does it not? I think that they are relatively optimized versions of each method... and then, a surprise struck.
[earino@localhost earino]$ ./first_char.pl Can't modify reverse in chop at ./first_char.pl line 27, near "$string +;" Execution of ./first_char.pl aborted due to compilation errors. [earino@localhost earino]$
Eek! What was that??? Can't modify reverse in chop... well, I wasn't trying to modify reverse, I was string to modify the reversed string. So, I did what any reasonable man with highly brilliant friends would do... I asked maverick... but he was in the shower :) So, jeffa was next... our first stab at it was:
return chop reverse scalar $str;
Which met with the same exact amount of success. After trying a few other things, we decided that this was a question better left for Seekers of Perl Wisdom... and let some of the wizards slap us a bit and tell us what we were doing wrong. So, undaunted by this first whack at the problem, I decided that I *still* wanted to benchmark the possible ways to get the first char out of a string, so this was my slightly lobotomized version 2 of the code (only change was in using_reverse_chop):
#!/usr/bin/perl -w use strict; use Benchmark; my $string = "a man, a plan, a canal, panama!"; sub using_regexp { $string =~ /^(.)/o; return $1; } sub using_split { return (split('', $string, 2))[0]; } sub using_substring { return substr($string, 0, 1); } sub using_unpack { return unpack('A1', $string); } sub using_reverse_chop { my $str = reverse $string; return chop $str; } timethese(-5, {'Using Regexp' => \&using_regexp, 'Using Split' => \&using_split, 'Using Substring' => \&using_substring, 'Using Unpack' => \&using_unpack, 'Using Reverse & Chop' => \&using_reverse_chop, });
As you can see, all I did was add a temporary value to store the reversed string... and though this was functional, I felt that it made it relatively useless for benchmarking, as I had the overhead of a brand new variable being created each time. So, I ran the benchmark for a minimum of 5 seconds of CPU time...the results are as follows:
Benchmark: running Using Regexp, Using Reverse & Chop, Using Split, Us +ing Substring, Using Unpack, each for at least 5 CPU seconds... Using Regexp: 5 wallclock secs ( 5.39 usr + -0.01 sys = 5.38 CPU) @ +207251.30/s (n=1115012) Using Reverse & Chop: 4 wallclock secs ( 5.23 usr + 0.00 sys = 5.23 + CPU) @ 255457.74/s (n=1336044) Using Split: 5 wallclock secs ( 5.25 usr + 0.01 sys = 5.26 CPU) @ 9 +7116.92/s (n=510835) Using Substring: 5 wallclock secs ( 5.25 usr + -0.02 sys = 5.23 CPU) + @ 1009882.22/s (n=5281684) Using Unpack: 5 wallclock secs ( 5.27 usr + 0.01 sys = 5.28 CPU) @ +260676.33/s (n=1376371)
And that, is just ugly... so, here is a nice table version of that...

MethodIterations
Regexp1,115,012
Reverse & Chop1,336,044
Split510,835
Substring5,281,684
Unpack1,376,371

So! Far and away, substring is the winner... which I guess makes sense, when you realize that substr is a function that exists to... well, get a substring! And getting the first character out of a string is pretty much a textbook case of that. The only real surprises were how *really* bad split did... but it makes sense, as it's creating a list. And how surpsisingly good R&C did even though I had to use a temporary variable (then again, I bet perl is incredibly smart about keeping that area of memory around handy in case it is needed again eh?) So... what's the moral of the story? Use the right tool for the right job. If you need a substring, use substr. And anyone that can explain to me why R&C failed, I'd love to hear it.

Replies are listed 'Best First'.
Re: First char out of a string & surprises with errors on chop reverse $string (that doesn't work!)
by merlyn (Sage) on Dec 26, 2001 at 21:43 UTC
    chop() wants an lvalue (like a variable), because it modifies its argument. The result of reverse is not an lvalue, so you lose.

    -- Randal L. Schwartz, Perl hacker

      Ok... we'd figured that far when jeffa got:
      sub bar { my $str = "foo"; return scalar reverse $str; } print chop bar(); captvanhalen: $ perl foo Can't modify non-lvalue subroutine call in chop at foo line 6, near " +);" Execution of foo aborted due to compilation errors.
      Now, my question is... that's not very "DWIM" of perl... (at least not to me!) Why does reverse not return an lvalue?
        For the same reason 2 + 2 doesn't return an lvalue.
        Besides, there's no reason for having reverse return an lvalue. TMTOWTDI, and reverse chop was never supposed to be one of them. Still, there are a lot of other WTDI.

        2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$

Re: First char out of a string & surprises with errors on chop reverse $string (that doesn't work!)
by maverick (Curate) on Dec 26, 2001 at 21:55 UTC
    merlyn is quite right...had I not been in the shower, I could have saved you the post :)

    For those of you who may not know what merlyn means by a lvalue...look at it this way. chop removes the last character from the scalar you pass it modifying the scalar. Since you passed it the return value from reverse, it didn't have a 'real' scalar to chop, thus the error.

    /\/\averick
    perl -l -e "eval pack('h*','072796e6470272f2c5f2c5166756279636b672');"

Re: First char out of a string & surprises with errors on chop reverse $string (that doesn't work!)
by ambrus (Abbot) on Apr 11, 2006 at 14:42 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2024-03-19 06:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found