[Newbie] Stupid problem need simple answer (Array & RegExp)

D

Daedalus

Keep in mind that I'm fresh one week years-old Perl newbie... thanks
I just need some information about what happen when :

my @string_list; #lets say that this array contain a couple of string, 3:
(the street, a book, theater)

foreach my $string (@string_list){
if ($string =~ s/(^the\b)|(^a\b)|//i){
$string_list[++$#string_lis] = $string
}
}

When I write: if ($string =~ s/(^the\b)|(^a\b)|//i)..., do I actually
replacing the string within @string_list (since $string is passed by
"foreach" from one of the string of @string_list) I'm asking cause I thought
that it would add the new strings to the array without touching what was
already there. So I would end with this list as @string_list (the street, a
book, theater, street, book).
But when try this, it overwrite and add. So it gives me ( street, book,
theater, street, book).
What am I doing wrong?

Thanks
DAE
 
D

Daedalus

Well I think I was guessing right cause if I write this instead:

foreach (@string_list){
my $string = $_;
if ($string =~ s/(^the\b)|(^a\b)//i){
$string_list[++$#string_lis] = $string
}
}
I get the wanted result, but I would like someone to confirm that.

thanks
DAE
 
J

Joe Smith

Daedalus said:
Keep in mind that I'm fresh one week years-old Perl newbie... thanks
I just need some information about what happen when :

my @string_list; #lets say that this array contain a couple of string, 3:
qw(the street, a book, theater);

foreach my $string (@string_list){
if ($string =~ s/(^the\b)|(^a\b)|//i){
$string_list[++$#string_lis] = $string
}
}

When I write: if ($string =~ s/(^the\b)|(^a\b)|//i)..., do I actually
replacing the string within @string_list (since $string is passed by
"foreach" from one of the string of @string_list)

Yes. You are modifying the array element as they are being processed
since the foreach variable is aliased to the array element and not
a copy of the array element.

The line inside the if() is better written as
push @string_list, $string;

-Joe
 
A

Anno Siegel

Daedalus said:
"Daedalus" <[email protected]> a écrit dans le message de [email protected]...

[top-post rearranged]
Keep in mind that I'm fresh one week years-old Perl newbie... thanks
I just need some information about what happen when :

my @string_list; #lets say that this array contain a couple of string, 3:
(the street, a book, theater)

foreach my $string (@string_list){
if ($string =~ s/(^the\b)|(^a\b)//i){
$string_list[++$#string_lis] = $string
}
}

When I write: if ($string =~ s/(^the\b)|(^a\b)|//i)..., do I actually
replacing the string within @string_list (since $string is passed by
"foreach" from one of the string of @string_list) I'm asking cause I thought
that it would add the new strings to the array without touching what was
already there. So I would end with this list as @string_list (the street, a
book, theater, street, book).
But when try this, it overwrite and add. So it gives me ( street, book,
theater, street, book).
What am I doing wrong?

Well I think I was guessing right cause if I write this instead:

foreach (@string_list){
my $string = $_;
if ($string =~ s/(^the\b)|(^a\b)//i){

What are the capturing parentheses in the pattern for? s/^the\b|^a\b//i
does the same thing.
$string_list[++$#string_lis] = $string
^^
Typo here. Don't re-type code, copy/paste it.

Also, "++$#string_list" would be better written as (scalar)
"@string_list".
}
}
I get the wanted result, but I would like someone to confirm that.

Your code (in all variants) has another problem. You are not supposed
to change an array while running a for-loop over it. (See "foreach" in
perlsyn, I suppose.) That it appears to work in this instance doesn't
mean it will with other versions of Perl.

There are lots of ways to repair this, one is

push @string_list, map /(?:the\b|a\b)?\s*(.*)/i, @string_list;

Instead of deleting the unwanted part, this captures the wanted part.
I have also changed the regex so that it also catches trailing
whitespace with the articles.

Anno
 
T

Thomas Kratz

Anno said:
Your code (in all variants) has another problem. You are not supposed
to change an array while running a for-loop over it. (See "foreach" in
perlsyn, I suppose.) That it appears to work in this instance doesn't
mean it will with other versions of Perl.

AFAIK you are not supposed to change the array, but changing the aliased
elements should be ok.

so:

push(@array, $_) for @array # not recommended

s/A/B/g for @array # ok

Right?

Thomas
 
A

Anno Siegel

Thomas Kratz said:
AFAIK you are not supposed to change the array, but changing the aliased
elements should be ok.

so:

push(@array, $_) for @array # not recommended

s/A/B/g for @array # ok

Right?

Right.

Anno
 
D

Daedalus

if ($string =~ s/(^the\b)|(^a\b)//i){
What are the capturing parentheses in the pattern for? s/^the\b|^a\b//i
does the same thing.

Actually It was for precedence, I wasn't sure about this \b|^ thing , it
doesn't harm, doesn't it ?
I'll have to familiarize with regexp precedence.

$string_list[++$#string_lis] = $string
^^
Typo here. Don't re-type code, copy/paste it.

Sorry I didn't know, but it makes sense to me now.
Your code (in all variants) has another problem. You are not supposed
to change an array while running a for-loop over it. (See "foreach" in
perlsyn, I suppose.) That it appears to work in this instance doesn't
mean it will with other versions of Perl.

Ok, about this, I wanted the foreach to look at the new string in the array
until there's nothing to add. So if the array start with one string like "to
the bookstore", and the the regexp is s/^to\b|^the\b//i, I would end with 3
string in the array: ("to the bookstore", "the bookstore", "bookstore"). I
need to extract all possible variants, so I don't want to process the array
only once and get rid of everything at the same time. There is probably a
better way to acheive this but that's the only one I found.

There are lots of ways to repair this, one is

push @string_list, map /(?:the\b|a\b)?\s*(.*)/i, @string_list;

Instead of deleting the unwanted part, this captures the wanted part.
I have also changed the regex so that it also catches trailing
whitespace with the articles.

Thank for that. It doesn't act exactly like I want since it would remove "to
the" at the same time, but it just reminds me of that push operator wich I
could use instead of "$string_list[++$#string_lis]" and I just learn the map
operator wich would avoid to overwrite the original array. Well I'm learning
and I like it! But for now (with my little knowledge) i'm still stuck with
writing the array from inside the for foreach.
 
D

Daedalus

Anno Siegel said:

Well the problem is that s///g don't do what I want, since it change every
thing at the same time.
Anyway (for now) it couldn't turn into an infinite loop, since the content
of the loop will inevitably stop adding to the array when it'll stop to find
matches (which is also inevitable, since even if all the words match, the
loop will end with an empty string)
I know it's not the perfect solution (and I'll try to find a better way
while i'm on my learning process), so if someone have any idea... I'd be
glad to correct this misuse.

DAE
 
D

Daedalus

Yes. You are modifying the array element as they are being processed
since the foreach variable is aliased to the array element and not
a copy of the array element.

Thanks, since it's an aliase that explain everything.

DAE
 
B

Brian McCauley

Daedalus said:
Well the problem is that s///g don't do what I want, since it change every
thing at the same time.

If you only want to change the first occurance in each string then
remove the /g.

If you want to append the changed version of the array to the original
array then why not:

s/A/B/g for my @array_copy = @array;
push @array, @array_copy;
Anyway (for now) it couldn't turn into an infinite loop, since the content
of the loop...

I asume you are talking about the effect of pushing into @array at the
same time with for().

How do you know that?

Bare in mind that the behaviour of for() when iterating over an array
that grows or shrinks is _undefined_. Undefined means anything could
happen, and also the thing that happens now may not be the thing that
happens in the next version of Perl.

If tomorrow the behaviour of pushing onto the array was to cause the
loop to reset to the start of the array then it could very well create
an infinite loop.
I know it's not the perfect solution (and I'll try to find a better way
while i'm on my learning process), so if someone have any idea... I'd be
glad to correct this misuse.

See several better solutions already given in this thread.

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

Daedalus

Your code (in all variants) has another problem. You are not
supposed
If you only want to change the first occurance in each string then
remove the /g.

If you want to append the changed version of the array to the original
array then why not:

s/A/B/g for my @array_copy = @array;
push @array, @array_copy;


I asume you are talking about the effect of pushing into @array at the
same time with for().

How do you know that?

Bare in mind that the behaviour of for() when iterating over an array
that grows or shrinks is _undefined_. Undefined means anything could
happen, and also the thing that happens now may not be the thing that
happens in the next version of Perl.

If tomorrow the behaviour of pushing onto the array was to cause the
loop to reset to the start of the array then it could very well create
an infinite loop.


See several better solutions already given in this thread.

Well sorry but at this time there no solutions in this thread that would
behave like I want.
Assuming that @string_list contain one string that is "to the bookstore"

foreach (@string_list){
my $string = $_;
push @string_list, $string if $string =~ s/(^to\b|^the\b)\s*//i;
}
this code would end with ("to the bookstore", "the bookstore", "bookstore")
and thats exactly the result i'm looking for.
While every other solutions proposed in the thread would end with ("to the
bookstore","the bookstore") or ("to the bookstore","bookstore") depending
wich of s///i or s///gi (or the map) is used.

Anyway if there's a way to do exactly the same thing with proper codes, it
would popup in my mind sooner or later, or in this newsgroup.

Thanks
DAE
 
S

Steven Kuo

On Thu, 24 Jun 2004, Daedalus wrote:

(snipped)
Well sorry but at this time there no solutions in this thread that would
behave like I want.
Assuming that @string_list contain one string that is "to the bookstore"

foreach (@string_list){
my $string = $_;
push @string_list, $string if $string =~ s/(^to\b|^the\b)\s*//i;
}
this code would end with ("to the bookstore", "the bookstore", "bookstore")
and thats exactly the result i'm looking for.
While every other solutions proposed in the thread would end with ("to the
bookstore","the bookstore") or ("to the bookstore","bookstore") depending
wich of s///i or s///gi (or the map) is used.

Anyway if there's a way to do exactly the same thing with proper codes, it
would popup in my mind sooner or later, or in this newsgroup.

Thanks
DAE




Try something like the algorithms descibed in

perldoc -q 'expand tabs'
and
perldoc -q 'nesting':


#!/usr/local/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @copy = my @strings = ( 'to the bookstore' );

for (@strings) {
push @copy, $_ while (s/^(to|the)\b\s*//i);
}

print Dumper \@copy;


__END__
$VAR1 = [
'to the bookstore',
'the bookstore',
'bookstore'
];
 
A

Anno Siegel

[more attributions lost]

[big snip]
Well sorry but at this time there no solutions in this thread that would
behave like I want.
Assuming that @string_list contain one string that is "to the bookstore"

foreach (@string_list){
my $string = $_;
push @string_list, $string if $string =~ s/(^to\b|^the\b)\s*//i;
}
this code would end with ("to the bookstore", "the bookstore", "bookstore")

No, it wouldn't. You'd need "while" instead of "if" inside the loop.
and thats exactly the result i'm looking for.

Well, then do it in a way that doesn't violate the rules. The most
obvious way (already suggested, I think), is to use a copy of the list
to loop over:

foreach ( my @copy = @string_list ) {
push @string_list, $_ while s/(^to\b|^the\b)\s*//i;
}

You don't need to copy $_ inside the loop because you already copied
all the values.
While every other solutions proposed in the thread would end with ("to the
bookstore","the bookstore") or ("to the bookstore","bookstore") depending
wich of s///i or s///gi (or the map) is used.

That would be mostly because your original example (about "a" and "the")
did in no way suggest this requirement.
Anyway if there's a way to do exactly the same thing with proper codes, it
would popup in my mind sooner or later, or in this newsgroup.

See above.

Anno
 
D

Daedalus

Try something like the algorithms descibed in
perldoc -q 'expand tabs'
and
perldoc -q 'nesting':


#!/usr/local/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @copy = my @strings = ( 'to the bookstore' );

for (@strings) {
push @copy, $_ while (s/^(to|the)\b\s*//i);
}

That's exactly what I want ... push into a copy and test the original
"while" instead of "if". (it's a shame... so simple).

Thanks
 
D

Daedalus

While every other solutions proposed in the thread would end with ("to
the
That would be mostly because your original example (about "a" and "the")
did in no way suggest this requirement.

Yeah that's totaly true. Actually when I posted the original exemple, I
didn't know that my foreach-loop violates the rules. My question was about
"When I write: if ($string =~ s/(^the\b)|(^a\b)|//i)..., do I'm actually
replacing the string within @string_list " And the answer was yes, in
"foreach my $string (@string_list)" $string become an aliase of the current
element of @string_list.
Then you make the point that my loop was illegal, so I had to change my
exemple to explain why I did use the loop that way, and see if someone could
point me to a legal way to obtain the same behavior... and here we are.


Finally, as I said in the topic, it was a stupid and simple thing: using
while with a copy (sorry I didn't thought of "while"). But in fact, why
should I thought of it ? Since I didn't realise the loop I did was illegal.


Don't worry, I'll just be a newb for a while ;)

Thanks to all (and sorry if I seem rude sometimes, english is not my primary
language)

DAE
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top