Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

foreach (each character in string..)?

by Anonymous Monk
on Jan 20, 2004 at 16:35 UTC ( [id://322621]=perlquestion: print w/replies, xml ) Need Help??

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

Anyone know how I could apply a 'foreach' on each character in a $string = "this is a test" or $string = "1234567890" ? I am trying to apply a random thing from a list to happen for each character in a string. Thanks.

Replies are listed 'Best First'.
Re: foreach (each character in string..)?
by duff (Parson) on Jan 20, 2004 at 16:37 UTC

    Usually you just split the string:

    for my $c (split //, $string) { ... }
Re: foreach (each character in string..)?
by hardburn (Abbot) on Jan 20, 2004 at 16:37 UTC

    Split it on nothing:

    foreach (split //, $string) { . . . }

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

Re: foreach (each character in string..)?
by robobunny (Friar) on Jan 20, 2004 at 16:38 UTC
    Here's one way:
    foreach(split('', $string)) { }
Re: foreach (each character in string..)?
by Roy Johnson (Monsignor) on Jan 20, 2004 at 16:53 UTC
    A couple other ways that don't make an arrayconstruct a list in memory of all the characters:
    for my $i (0..length($string)-1) { # whatever with substr($string, $i, 1) }
    or
    while ($string =~ /./gs) { # whatever with $& # note that this incurs the penalty for using $& }

    The PerlMonk tr/// Advocate
      while ($string =~ /./gs) {
          # whatever with $&
          # note that this incurs the penalty for using $&
      }
      
      You can avoid the $& penalty by using parentheses: /(.)/gs and using the $1 variable.

      Arjen

        You can avoid the $& penalty by using parentheses: /(.)/gs and using the $1 variable.

        Just to clarify ... you can avoid the $& penalty on all your other regular expressions, but you still pay it on this one (only its name is changed to $1)

      I tried to send you a /msg, but I am unsure how to do so due to the space in your username. I have a quick pedantic note: you should probably s/array/list/ on your first sentence. 8^)

      Even more pedantic is to point out that 0..length($string)-1 does indeed make a list, but I think we know what you meant. ;)

      Update: I stand corrected. It might be noted that I knew there was an optimization with for and the range operator, but I thought it still ended up building a list -- I didn't realize it just turned into an iterator.

        Yes, I am a bit loose in calling lists arrays. But note that in a foreach, the .. operator with integer operands does NOT create a list. It acts as an iterator. This is a special optimization introduced in 5.005.

        The PerlMonk tr/// Advocate
Re: foreach (each character in string..)?
by KPeter0314 (Deacon) on Jan 20, 2004 at 16:40 UTC
    Just split it out...<apologies in advance for the pun>
    $string = "this is a test"; foreach $char (split //, $string) { print "$char\n"; }

    -Kurt

Re: foreach (each character in string..)?
by broquaint (Abbot) on Jan 20, 2004 at 16:56 UTC
    If you happen to be working on a particularly large string you can just inch your way across which does away with the temporary list e.g
    while($str =~ /\G(.)/gs) { ... } ## or with substr() my $i = 0; while( defined(my $c = substr $str, $i++, 1 )) { ... }
    See. perlop, perlre and substr for more info.
    HTH

    _________
    broquaint

Re: foreach (each character in string..)?
by robobunny (Friar) on Jan 20, 2004 at 16:42 UTC
    OK, since I was late with my legit response, here's a stupid way:
    my @list; my $string = "hi there"; while($string) { push @list, chop($string); } foreach(reverse(@list)) { print "$_\n"; }

      make it smart-stupid with unshift.
      really not adding anything,
      boo

        To be honest, I'm amazed no one has bitched because it fails if you hand it the string '0', but thanks anyway :)
Re: foreach (each character in string..)?
by l3nz (Friar) on Jan 20, 2004 at 17:30 UTC
    Just for a change, a different (less perlish, maybe) way is to use an index to extract a single character, more or less like this:
    my $c; for ( my $i = 0; $i < length($string); $i++ ) { $c = substr( $string, $i, 1); }
    I would not usually favour it for style and readability that are much better in the split version, but I posted it for completeness.

    By the way, I noticed that the execution speed is more or less the same; I measured this with Benchmark, and noticed that the longer the string in $string, the comparatively fast the for version gets.

    See how the bigger the input $string gets, the bigger the difference in favour of the for version is, going from +5% to +16% in the 30k case.

    $string is 3000 bytes Benchmark: running for, split, each for at least 10 CPU seconds... for: 10 wallclock secs (10.31 usr + 0.00 sys = 10.31 CPU) @ 12 +3.73/s (n=1275) split: 10 wallclock secs (10.58 usr + 0.00 sys = 10.58 CPU) @ 11 +7.15/s (n=1240) +5% $string is 10000 bytes Benchmark: running for, split, each for at least 10 CPU seconds... for: 11 wallclock secs (10.33 usr + 0.00 sys = 10.33 CPU) @ 36 +.96/s (n=382) split: 11 wallclock secs (10.44 usr + 0.00 sys = 10.44 CPU) @ 34 +.08/s (n=356) +8% $string is 30000 bytes Benchmark: running for, split, each for at least 10 CPU seconds... for: 10 wallclock secs (10.19 usr + 0.00 sys = 10.19 CPU) @ 12 +.36/s (n=126) split: 10 wallclock secs (10.60 usr + 0.00 sys = 10.60 CPU) @ 10 +.57/s (n=112) +16%
    This is my test code:
Re: foreach (each character in string..)?
by BrowserUk (Patriarch) on Jan 21, 2004 at 00:21 UTC

    If you need to play with long strings char-by-char, spliting and joining them can consume large amounts of memory, and making every reference to each char of the form substr( $s, $p, 1 ) = 'X' if substr( $s, $p, 1 ) ne ' '; can be a PITA.

    You can avoid both using lvalue substr refs.

    my $s= 'the quick brown fox jumps over the lazy dog'; $$_ ne ' ' and $$_ = 'X' for map{ \substr( $s,$_, 1 ) } 0 .. length $s; print $s; XXX XXXXX XXXXX XXX XXXXX XXXX XXX XXXX XXXX

    I'm still hoping that LW will see fit to include $s[ $p ] syntax for accessing the chars of a string in P6 once that syntax is no longer valid for elements of an array -- or at least that it will be possible to create a module that gives this syntax efficiently.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    Timing (and a little luck) are everything!

Re: foreach (each character in string..)?
by ambrus (Abbot) on Jan 21, 2004 at 13:56 UTC

    Why does evryone use split? I think the best way is

    for $x ($string=~/(.)/g) { &WHATEVER ($x); }

    Also, if you just want to iterate over characters, you can use

    while ($string=~/(.)/g) { &WHATEVER ($1); }

    Update: see also thread FMTYEWTK about split // about split //

    Update: of course while ($string=~/(.)/gs) is more correct, note /gs

      I think the best way is
      What's your definition of "best way"? split // seems to be the most common idiom. And the "ugly" way, use of substr seems to be the fastest according to the following benchmark:
      #!/usr/bin/perl use strict; use warnings; use Benchmark qw /timethese cmpthese/; our $char; for my $size (4, 16, 64, 256, 1024) { print "Iterating over a string of length $size.\n"; our $str = "-" x $size; cmpthese -5 => { split => 'for my $c (split // => $str) {$char = $c}', for => 'for my $c ($str =~ /(.)/g) {$char = $c}', while => 'while ($str =~ /(.)/g) {$char = $1}', substr => 'for my $i (0 .. (length ($str) - 1)) {$char = substr $str +, $i, 1}', } } __END__ Iterating over a string of length 4. Rate split for while substr split 157193/s -- -9% -20% -44% for 173109/s 10% -- -12% -38% while 197371/s 26% 14% -- -30% substr 280339/s 78% 62% 42% -- Iterating over a string of length 16. Rate split for while substr split 47828/s -- -3% -12% -55% for 49213/s 3% -- -10% -54% while 54413/s 14% 11% -- -49% substr 107090/s 124% 118% 97% -- Iterating over a string of length 64. Rate split for while substr split 12882/s -- -5% -7% -56% for 13574/s 5% -- -2% -54% while 13798/s 7% 2% -- -53% substr 29595/s 130% 118% 114% -- Iterating over a string of length 256. Rate split for while substr split 3244/s -- -5% -7% -59% for 3420/s 5% -- -2% -57% while 3483/s 7% 2% -- -56% substr 7951/s 145% 132% 128% -- Iterating over a string of length 1024. Rate split for while substr split 818/s -- -4% -7% -59% for 850/s 4% -- -3% -58% while 875/s 7% 3% -- -57% substr 2013/s 146% 137% 130% --
      I must say, the performance of substr surprises me, and I find the difference between substr and other methods suspect, but I can't find any flaw in the benchmark.

      Abigail

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (None)
    As of 2024-04-25 04:25 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found