Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
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 studying the Monastery: (6)
As of 2024-04-21 11:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found