Re: foreach (each character in string..)?
by duff (Parson) on Jan 20, 2004 at 16:37 UTC
|
for my $c (split //, $string) {
...
}
| [reply] [d/l] [select] |
Re: foreach (each character in string..)?
by hardburn (Abbot) on Jan 20, 2004 at 16:37 UTC
|
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
| [reply] [d/l] [select] |
Re: foreach (each character in string..)?
by robobunny (Friar) on Jan 20, 2004 at 16:38 UTC
|
foreach(split('', $string)) { }
| [reply] [d/l] |
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
| [reply] [d/l] [select] |
|
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
| [reply] [d/l] [select] |
|
| [reply] [d/l] [select] |
|
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.
| [reply] [d/l] [select] |
|
| [reply] |
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";
}
| [reply] [d/l] |
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.
| [reply] [d/l] |
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";
}
| [reply] [d/l] |
|
| [reply] |
|
To be honest, I'm amazed no one has bitched because it fails if you hand it the string '0', but thanks anyway :)
| [reply] |
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:
| [reply] [d/l] [select] |
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.
| [reply] [d/l] [select] |
Re: foreach (each character in string..)?
by ambrus (Abbot) on Jan 21, 2004 at 13:56 UTC
|
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 | [reply] [d/l] [select] |
|
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 | [reply] [d/l] |
|
| [reply] |