Remove first letter of each string in array

J

Julia deSilva

Hi there all.

my @array = qw(1this 2is 3a 4new 5message);

for (my $x=0;$x<@array;$x++){
$array[$x] = substr($array[$x],1,length($array[$x]))
}

foreach (@array){print"$_\n";};

This does indeed remove the first letter of each string in this array, but
is there a better way ! ?

Thanks for your help in advance

J
 
J

Jürgen Exner

Julia said:
Hi there all.

my @array = qw(1this 2is 3a 4new 5message);

for (my $x=0;$x<@array;$x++){

Why not just
foreach(@array) {
$array[$x] = substr($array[$x],1,length($array[$x]))

and then a simple
s/.//;
}

foreach (@array){print"$_\n";};

This does indeed remove the first letter of each string in this
array,

Actually it doesn't. Your code removes the first character in each string,
not the first letter.
If you want to remove the first letter, then you can use
s/[a-zA-Z]//;
but is there a better way ! ?

"Better" in which respect?
Faster? Consumes less memory? More "perlish"? Shorter code (Perl golf comes
to mind)?

jue
 
K

Kien Ha

Julia said:
Hi there all.

my @array = qw(1this 2is 3a 4new 5message);

for (my $x=0;$x<@array;$x++){
$array[$x] = substr($array[$x],1,length($array[$x]))
}

foreach (@array){print"$_\n";};

This does indeed remove the first letter of each string in this array, but
is there a better way ! ?

Shorter and more Perlish:
$_ = substr $_, 1 for @array;
or
@array = map { substr $_, 1 } @array;
 
T

Tore Aursand

my @array = qw(1this 2is 3a 4new 5message);

for (my $x=0;$x<@array;$x++){
$array[$x] = substr($array[$x],1,length($array[$x]))
}

foreach (@array){print"$_\n";};

This does indeed remove the first letter of each string in this array, but
is there a better way ! ?

Use Perl's 'for' or 'foreach';

for ( @array ) {
$_ = substr( $_, 1 );
}
 
J

Jim Cochrane

Hi there all.

my @array = qw(1this 2is 3a 4new 5message);

for (my $x=0;$x<@array;$x++){
$array[$x] = substr($array[$x],1,length($array[$x]))
}

foreach (@array){print"$_\n";};

This does indeed remove the first letter of each string in this array, but
is there a better way ! ?

Thanks for your help in advance


I'm sure the other replies give you some good solutions, but I needed some
Perl practice, so here's one of the many other ways to do it:

#!/usr/bin/perl

use strict;
use warnings;

my @array = qw(1this 2is 3a 4new 5message);

@array = map {
/^.(.*)/;
} @array;

foreach (@array){print"$_\n";};
 
A

! aaa

reverse() and then chop() might be more efficient ?

otherwise, the whole thing need to get shifted around in memory all the
time.
 
U

Uri Guttman

KH> Julia deSilva said:
Hi there all.
my @array = qw(1this 2is 3a 4new 5message);
for (my $x=0;$x<@array;$x++){
$array[$x] = substr($array[$x],1,length($array[$x]))
}
foreach (@array){print"$_\n";};
This does indeed remove the first letter of each string in this
array, but
is there a better way ! ?

KH> Shorter and more Perlish:
KH> $_ = substr $_, 1 for @array;
KH> or
KH> @array = map { substr $_, 1 } @array;

and even faster and simpler (IMO):
substr( $_, 0, 1, '' ) for @array;

uri
 
U

Uri Guttman

"!a" == ! aaa <!> writes:

!a> reverse() and then chop() might be more efficient ?
!a> otherwise, the whole thing need to get shifted around in memory all the
!a> time.

not exactly. perl is smart about losing stuff from the beginning of
strings and arrays. it moves a pointer to beyond the deleted entries and
doesn't shift down the rest unless it thinks it needs to do a realloc.

uri
 
B

Brian McCauley

Kien Ha said:
Julia deSilva wrote:
my @array = qw(1this 2is 3a 4new 5message);
for (my $x=0;$x<@array;$x++){
$array[$x] = substr($array[$x],1,length($array[$x]))
}

Shorter and more Perlish:
$_ = substr $_, 1 for @array;
or
@array = map { substr $_, 1 } @array;

Note: those do different things. The first alters the existing
elements of @array the second replaces them with new elements. This
difference may be significant if there are other references floating
about to the elements of @array. The second is also a lot slower.

Arguably it's even more ideomatic to say.

substr $_, 0, 1, '' for @array;

or

substr($_, 0, 1) = '' for @array;


However, it's not so fast...

use Benchmark;
my @data = ( 'aaa' ... 'zzz' );
timethese 100 => {
substr1 => sub {
my @array = @data;
$_ = substr $_, 1 for @array;
},
substr2 => sub {
my @array = @data;
substr $_, 0, 1, '' for @array;
},
substr3 => sub {
my @array = @data;
substr($_, 0, 1) = '' for @array;
},
map => sub {
my @array = @data;
@array = map { substr $_, 1 } @array;
},
};

__END__
Benchmark: timing 100 iterations of map, substr1, substr2, substr3...
map: 7 wallclock secs ( 7.59 usr + 0.00 sys = 7.59 CPU) @ 13.18/s (n=100)
substr1: 4 wallclock secs ( 3.89 usr + 0.00 sys = 3.89 CPU) @ 25.71/s (n=100)
substr2: 4 wallclock secs ( 4.15 usr + 0.00 sys = 4.15 CPU) @ 24.10/s (n=100)
substr3: 5 wallclock secs ( 5.14 usr + 0.00 sys = 5.14 CPU) @ 19.46/s (n=100)

--
\\ ( )
. _\\__[oo
.__/ \\ /\@
. l___\\
# ll l\\
###LL LL\\
 
T

Tore Aursand

reverse() and then chop() might be more efficient ?

That _might_ be more effective than using a regular expression, but in
this case 'substr' is far superior.

And - why use 'reverse' and 'chop'? Do you have a working example?
 
U

Uri Guttman

BM> However, it's not so fast...

you chose a bad data set for this.

BM> use Benchmark;
BM> my @data = ( 'aaa' ... 'zzz' );

those strings are too short. the copy is quick and so it looks like it
beats the 4 arg substr. try this data:

my @data = ( 'a' x 1000 ) x 1000 ;

Benchmark: running map, substr1, substr2, substr3 for at least 2 CPU seconds...
map: 2 wallclock secs ( 2.22 usr + 0.00 sys = 2.22 CPU) @ 127.48/s (n=283)
substr1: 2 wallclock secs ( 2.18 usr + 0.00 sys = 2.18 CPU) @ 293.12/s (n=639)
substr2: 2 wallclock secs ( 2.07 usr + 0.00 sys = 2.07 CPU) @ 347.34/s (n=719)
substr3: 2 wallclock secs ( 2.05 usr + 0.00 sys = 2.05 CPU) @ 317.07/s (n=650)

now 4 arg substr is much faster.

this is why benchmarking is an art unto itself. it is so easy to get
results that support wrong conclusions.

uri
 
B

Brian McCauley

Uri Guttman said:
BM> However, it's not so fast...

you chose a bad data set for this.
!?

my @data = ( 'a' x 1000 ) x 1000 ;

Benchmark: running map, substr1, substr2, substr3 for at least 2 CPU seconds...
map: 2 wallclock secs ( 2.22 usr + 0.00 sys = 2.22 CPU) @ 127.48/s (n=283)
substr1: 2 wallclock secs ( 2.18 usr + 0.00 sys = 2.18 CPU) @ 293.12/s (n=639)
substr2: 2 wallclock secs ( 2.07 usr + 0.00 sys = 2.07 CPU) @ 347.34/s (n=719)
substr3: 2 wallclock secs ( 2.05 usr + 0.00 sys = 2.05 CPU) @ 317.07/s (n=650)

now 4 arg substr is much faster.

this is why benchmarking is an art unto itself. it is so easy to get
results that support wrong conclusions.

Indeed - in this case you've chosen the conclusion you want and
written a benchmark to support it. This ensures the conclusion that
will be supported is the "right" one.

Removing the first character from an array of string is typically
something one does to plaintext. And plaintext is typically ~70-120
characters wide.

Without knowing the OP's real problem domain it's just as likely that
it is you who've chosen a bad data set that supports the wrong
conclusion. :)

But then again I'm not even able to reproduce your results...

use Benchmark;

my %tests;
for ( my $i = 1; $i <= 1024 ; $i*=2 ) {
my @data = ('X' x $i) x 1000;
$tests{sprintf('4arg%4d',$i)} = sub {
my @array = @data;
substr $_, 0, 1, '' for @array;
};
$tests{sprintf('copy%4d',$i)} = sub {
my @array = @data;
$_ = substr $_, 1 for @array;
};
}

timethese -2 => \%tests;
__END__
Benchmark: running 4arg 1, 4arg 2, 4arg 4, 4arg 8, 4arg 16, 4arg 32, 4arg 64, 4arg 128, 4arg 256, 4arg 512, 4arg1024, copy 1, copy 2, copy 4, copy 8, copy 16, copy 32, copy 64, copy 128, copy 256, copy 512, copy1024, each for at least 2 CPU seconds...
4arg 1: 2 wallclock secs ( 2.19 usr + 0.00 sys = 2.19 CPU) @ 642.01/s (n=1406)
4arg 2: 2 wallclock secs ( 2.02 usr + 0.00 sys = 2.02 CPU) @ 642.57/s (n=1298)
4arg 4: 2 wallclock secs ( 2.19 usr + 0.00 sys = 2.19 CPU) @ 642.01/s (n=1406)
4arg 8: 2 wallclock secs ( 2.16 usr + 0.00 sys = 2.16 CPU) @ 592.13/s (n=1279)
4arg 16: 2 wallclock secs ( 2.16 usr + 0.00 sys = 2.16 CPU) @ 592.13/s (n=1279)
4arg 32: 3 wallclock secs ( 2.13 usr + 0.00 sys = 2.13 CPU) @ 572.77/s (n=1220)
4arg 64: 2 wallclock secs ( 2.16 usr + 0.00 sys = 2.16 CPU) @ 518.06/s (n=1119)
4arg 128: 2 wallclock secs ( 2.03 usr + 0.00 sys = 2.03 CPU) @ 354.19/s (n=719)
4arg 256: 2 wallclock secs ( 2.06 usr + 0.00 sys = 2.06 CPU) @ 229.13/s (n=472)
4arg 512: 2 wallclock secs ( 2.02 usr + 0.09 sys = 2.11 CPU) @ 135.55/s (n=286)
4arg1024: 2 wallclock secs ( 1.74 usr + 0.27 sys = 2.01 CPU) @ 77.61/s (n=156)
copy 1: 2 wallclock secs ( 2.06 usr + 0.00 sys = 2.06 CPU) @ 702.91/s (n=1448)
copy 2: 2 wallclock secs ( 2.06 usr + 0.00 sys = 2.06 CPU) @ 702.91/s (n=1448)
copy 4: 2 wallclock secs ( 2.07 usr + 0.00 sys = 2.07 CPU) @ 699.52/s (n=1448)
copy 8: 3 wallclock secs ( 2.01 usr + 0.00 sys = 2.01 CPU) @ 645.77/s (n=1298)
copy 16: 2 wallclock secs ( 2.06 usr + 0.03 sys = 2.09 CPU) @ 606.70/s (n=1268)
copy 32: 2 wallclock secs ( 2.15 usr + 0.00 sys = 2.15 CPU) @ 594.88/s (n=1279)
copy 64: 2 wallclock secs ( 2.08 usr + 0.00 sys = 2.08 CPU) @ 535.10/s (n=1113)
copy 128: 2 wallclock secs ( 2.12 usr + 0.00 sys = 2.12 CPU) @ 339.15/s (n=719)
copy 256: 3 wallclock secs ( 2.09 usr + 0.00 sys = 2.09 CPU) @ 207.66/s (n=434)
copy 512: 2 wallclock secs ( 2.11 usr + 0.00 sys = 2.11 CPU) @ 135.55/s (n=286)
copy1024: 2 wallclock secs ( 2.11 usr + 0.00 sys = 2.11 CPU) @ 79.15/s (n=167)

--
\\ ( )
. _\\__[oo
.__/ \\ /\@
. l___\\
# ll l\\
###LL LL\\
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top