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

Discussion in 'Perl Misc' started by Daedalus, Jun 24, 2004.

  1. Daedalus

    Daedalus Guest

    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
     
    Daedalus, Jun 24, 2004
    #1
    1. Advertising

  2. Daedalus

    Daedalus Guest

    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


    "Daedalus" <> a écrit dans le message de news:
    BsvCc.530$...
    > 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
    >
    >
     
    Daedalus, Jun 24, 2004
    #2
    1. Advertising

  3. Daedalus

    Joe Smith Guest

    Daedalus wrote:

    > 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
     
    Joe Smith, Jun 24, 2004
    #3
  4. Daedalus

    Anno Siegel Guest

    Daedalus <> wrote in comp.lang.perl.misc:
    > "Daedalus" <> a écrit dans le message de news:
    > BsvCc.530$...


    [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
     
    Anno Siegel, Jun 24, 2004
    #4
  5. Daedalus

    Thomas Kratz Guest

    Anno Siegel wrote:

    >
    > 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

    --
    open STDIN,"<&DATA";$=+=14;$%=50;while($_=(seek( #J~.> a>n~>>e~.......>r.
    STDIN,$:*$=+$,+$%,0),getc)){/\./&&last;/\w| /&&( #.u.t.^..oP..r.>h>a~.e..
    print,$_=$~);/~/&&++$:;/\^/&&--$:;/>/&&++$,;/</ #.>s^~h<t< ..~. ...c.^..
    &&--$,;$:%=4;$,%=23;$~=$_;++$i==1?++$,:_;}__END__#....>>e>r^..>l^...>k^..
     
    Thomas Kratz, Jun 24, 2004
    #5
  6. Daedalus

    Anno Siegel Guest

    Thomas Kratz <> wrote in comp.lang.perl.misc:
    > Anno Siegel wrote:
    >
    > >
    > > 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?


    Right.

    Anno
     
    Anno Siegel, Jun 24, 2004
    #6
  7. Daedalus

    Daedalus Guest

    > > 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.


    > Anno
     
    Daedalus, Jun 24, 2004
    #7
  8. Daedalus

    Daedalus Guest

    "Anno Siegel" <-berlin.de> a écrit dans le message de
    news: cbeg3h$gii$-Berlin.DE...
    > Thomas Kratz <> wrote in comp.lang.perl.misc:
    > > Anno Siegel wrote:
    > >
    > > >
    > > > 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?

    >
    > Right.


    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
     
    Daedalus, Jun 24, 2004
    #8
  9. Daedalus

    Daedalus Guest

    > 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
     
    Daedalus, Jun 24, 2004
    #9
  10. "Daedalus" <> writes:

    > "Anno Siegel" <-berlin.de> a écrit dans le message de
    > news: cbeg3h$gii$-Berlin.DE...
    > > Thomas Kratz <> wrote in comp.lang.perl.misc:
    > > > Anno Siegel wrote:
    > > >
    > > > >
    > > > > 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?

    > >
    > > Right.

    >
    > 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\\
     
    Brian McCauley, Jun 24, 2004
    #10
  11. Daedalus

    Daedalus Guest

    > > > > > 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?
    > > >
    > > > Right.

    > >
    > > 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.


    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
     
    Daedalus, Jun 24, 2004
    #11
  12. Daedalus

    Steven Kuo Guest

    On Thu, 24 Jun 2004, Daedalus wrote:

    (snipped)
    > >
    > > 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.

    >
    > 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'
    ];

    --
    Hope this helps,
    Steven
     
    Steven Kuo, Jun 24, 2004
    #12
  13. Daedalus

    Anno Siegel Guest

    Daedalus <> wrote in comp.lang.perl.misc:
    [more attributions lost]

    [big snip]

    > > 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.

    >
    > 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
     
    Anno Siegel, Jun 24, 2004
    #13
  14. Daedalus

    Daedalus Guest

    > 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
     
    Daedalus, Jun 25, 2004
    #14
  15. Daedalus

    Daedalus Guest

    Thanks & End of the story

    >> 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.


    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
     
    Daedalus, Jun 25, 2004
    #15
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Daniel Frey

    Simple Question - Simple Answer?

    Daniel Frey, Dec 28, 2004, in forum: XML
    Replies:
    4
    Views:
    873
    Daniel Frey
    Jan 12, 2005
  2. Brandon McCombs
    Replies:
    4
    Views:
    535
    Richard Wheeldon
    Aug 28, 2006
  3. Greg Corradini

    Simple mx.ODBC prob seeks simple answer

    Greg Corradini, Apr 6, 2007, in forum: Python
    Replies:
    0
    Views:
    333
    Greg Corradini
    Apr 6, 2007
  4. rincewind

    stupid, STUPID question!

    rincewind, Apr 19, 2009, in forum: HTML
    Replies:
    25
    Views:
    1,059
  5. Joao Silva
    Replies:
    16
    Views:
    407
    7stud --
    Aug 21, 2009
Loading...

Share This Page