Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Huge simple problem

by Anonymous Monk
on Aug 05, 2009 at 22:14 UTC ( #786243=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

When I run the following,
$query = "bush and clinton and bush and obama and huckabee"; @searchTexts = split (/and|or/i,$query); for ($i = 0; $i <= @searchTexts +1 -1; $i++) { @searchTexts[$i] =~ s/^\s+//; @searchTexts[$i] =~ s/\s+$//; print @searchTexts[$i]."\n"; }
the program loops endlessly. What am I doing wrong? Thanks in advance

Comment on Huge simple problem
Download Code
Re: Huge simple problem
by Anonymous Monk on Aug 05, 2009 at 22:23 UTC
    Never mind. I see my problem now. Duh! The <= should be <. And to think that I spent over an hour on that.
      Perhaps the Basic debugging checklist would help shorten the debug cycle in the future, particularly tips #1 and #2:

      #1 use warnings; would have spewed many warnings which may have lead you to your problem.

      #2 print "i = $i\n"; inside your for loop

      I see my problem now...

      Bonus points if you actually explain why using '<=' makes the loop run "endlessly" (until available memory is exhausted, that is)   :)

      (Probably obvious to most monks, but maybe not to everyone...)

        Bonus points if you actually explain why
        Since the OP has not chimed in, I will make an attempt at an explanation.
        Probably obvious to most monks
        I freely admit that it was not obvious to me. I'm glad you prompted me to think about it further.

        Here goes. It is a case of modifying an array in a for loop. Before the for loop, the @searchTexts array contains 5 elements (indices: 0-4). The 1st 5 times though the loop, the array size remains constant at 5. Now, let's look at the end condition of the loop:

        $i <= @searchTexts +1 -1;

        which is just a slightly obfuscated form of:

        $i <= @searchTexts;

        Since the array is evaluated in scalar context, @searchTexts is the same as scalar @searchTexts, which returns the current size of the array. The 1st 5 times through the loop, the size is 5, and therefore the end condition is satisfied (the 5th time , $i is 4, which is less than or equal to 5). At the end of the 5th time through the loop, $i increments to 5, which is still less than or equal to 5. This causes the loop to be executed a 5th 6th time, which in turn causes the array to grow by 1 because of this line:

        @searchTexts[$i] =~ s/^\s+//;

        which resolves to this:

        @searchTexts[5] =~ s/^\s+//;

        From there on, the array continues to grow by one each time through the loop because the end condition is dependent on the array size.

Re: Huge simple problem
by dreadpiratepeter (Priest) on Aug 05, 2009 at 22:43 UTC
    May I suggest that you learn to use the foreach loop in Perl, rather than the c-style for loop.
    my @searchTexts = split (/and|or/i,$query); foreach my $text (@searchtexts) { $text =~ s/^\s+//; $text =~ s/\s+$//; print "$text\n"; }
    and in a loop this simple I would just use $_
    foreach (split /and|or/i,$query) { s/^\s*(.*?)\s*$/$1/; print "$_\n"; }
    or reduce it even further with a map and a join (depends on circumstance, if I'm the not only maintainer I'd probably use the above version)
    print join("\n",map {/^\s*(.*?)\s*$/;$1} split(/and|or/i,$query)) . "\ +n";
    all the above code is untested.

    update: also you could first clean the ends of $query (with s/^\s*(.*?)\s*$/). Then if you change the slip condition to /\s*(?:and|or)\s*/ you can skip the map:
    print join("n\",split(/\s*(?:and|or)\s*/,$query)) . "\n";
    of course at this point you can skip the whole split and use:
    $query =~ s/\s*(?:and|or)\s*/\n/g; print "$query\n";
    A final note, all these will fail horribly if your data has and/or imbedded in words. ie world. You probably want to split on /\s+(?:and|or)\s+/ or /\s*\b(?:and|or)\b\s*/

    Phew, now I need a nap

    "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."
      Thanks for the help!
      May I suggest that you learn to use the foreach loop in Perl, rather than the c-style for loop.

      Do note that foreach is just an alternate spelling of for. for my $text (@searchtexts) { ... } is exactly equivalent to foreach my $text (@searchtexts) { ... }.

      Either way, though, I can't remember the last time I used a C-style for loop in Perl. for ( [list] ) is so much nicer and avoids all those nasty off-by-one errors.

        I second this advice, and (missingthepoint posting anonymously), ++ to you both for helpful replies (when I remember what I changed my password to).

        Also, may I suggest you use strict and use warnings? (put those two lines at the top of your code). It will save you a lot of time - I speak from painful experience :|

        One final word: it appears as though you're trying to get the number of elements in @a by writing @a +1 -1. That's contorted - you can just say $i <= @a, since <= puts its operands (that is, $i and @a) in scalar context, and for the array, produces the number of elements it contains.

        You can improve that even further, too. As it stands you have an off-by-one error - looping from $i = 0 to $i = @array<c> will put you one element beyond the end. If you need a C-style for loop, you can write: <c>for ($i = 0; $i < @a; $i++) { ... and be done with it. :)

        But in your situation, you would be better served just doing what dsheroh++ said and using a plain for: for my $element (@a) { <do something with element> ...

        Don't hesitate to let me know if that was unclear. :)

        I always say foreach when doing (@a) type loops and for when doing c-style loops. helps make the code a little more self-documenting

        "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."
Re: Huge simple problem
by leighsharpe (Monk) on Aug 06, 2009 at 03:37 UTC
    Shouldn't that be:
    $searchTexts[$i] =~ s/^\s+//; $searchTexts[$i] =~ s/\s+$//; print $searchTexts[$i]."\n";
    Instead of:
    @searchTexts[$i] =~ s/^\s+//; @searchTexts[$i] =~ s/\s+$//; print @searchTexts[$i]."\n";

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://786243]
Approved by ww
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (13)
As of 2015-05-24 08:01 GMT
Find Nodes?
    Voting Booth?

    In my home, the TV remote control is ...

    Results (471 votes), past polls