Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Breaking from a foreach loop, returning to position

by Anonymous Monk
on Aug 26, 2015 at 14:46 UTC ( [id://1140045]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

So, I'm running into some trouble thinking of how to temporarily leave a for loop, only to return to it later. Ideally I'd like to return to the spot where I left off. I feel like there's some logic I'm missing somewhere.

To start, let's say I have:
-------

#some stuff foreach (@array) { $spot_in_array = $_; #some stuff if(CONDITION_MET){ #temporarily leave foreach } }

In the if statement, how would I leave the foreach loop without leaving permanently? I thought of using last but I know that wouldn't get me back into the loop. The reason why I added that $spot = $_; is because, I think I'm on the right track here, I may be able to use a for loop that starts at $spot and ends at the last index of @array. How I'd do this kind of stumps me. Would I start with a foreach loop at all? Or do I set $spot to the first item, and iterate through a for loop, in a nested while loop that returns every time if(!CONDITION_MET) happens, kind of like an on/off switch.

I've only picked up and been working with Perl for a few weeks, so there's a lot I still don't know. Any help would be greatly appreciated. Thanks.

Replies are listed 'Best First'.
Re: Breaking from a foreach loop, returning to position
by stevieb (Canon) on Aug 26, 2015 at 14:52 UTC

    This sounds like an XY Problem.

    Please explain what it is you want to do while you're outside of the loop. In fact, explain what you're trying to achieve overall.

    It sounds to me that what you might want to do is call a function if the condition is met instead of leaving the loop and going back in. That feels a lot like a goto type of thing.

      Hmmm, yeah I suppose I could have worded the question better and focus more on that.
      To give a bigger idea, this is similar to a debugger. It reads in lines from a file and executes them. I have a foreach loop going through each line of the file, doing some work on it, and continuing. I have a button (Perl/Tk) that, ideally, will halt the process, and when clicked again it continues the process. This needs to be non-blocking, as there are several other processes running at the same time. Outside of the foreach loop, a number of things could be done that pertain to the full program so I feel it's arbitrary, but the file that is being executed should pause on the current line.
      So, for example, say the file contains:
      --------------

      print "Abc\n"; print "123\n"; print "I'm in a loop\n"; ...
      etc. etc.
      The goal is to loop it through, executing those commands and doing some other operations (this is already implemented), and when I click the "Halt" button, it'll pause while the rest of the program continues it's normal operations. When I hit the button again, it'll continue from where it left off.
      Hope that gave a better idea of what I'm trying to accomplish.

      Since I can't find an edit button...
      I am on Perl 5.10, can't upgrade, and do not have access to downloading different packages and modules.

        The Monastery's moral equivalent of an "edit button" involves a couple steps (but ONLY for a registered Monk -- AM's need not apply):

        1. open the node you wish to edit (so it's the top item in the browser window or tab)
        2. scroll down below the formatted version of what you originally entered to see --
                      ...VOILA!
          a text-entry box (just like the one where you entered your original)
        3. Edit -- DO NOT DELETE WHAT YOU'VE POSTED -- simply use <strike>...</strike> around anything you wish to disavow (code: add a comment in the erroneously posted code or create a new code block and put the strike tags outside the original code tags). Be sure to add a note -- something like "<p><b>UPDATE: </b> (tell us here what you changed and, if relevant, why).</p>"
        4. Use the "update" button but only after carefully reviewing the text-entry box because there is NO preview. I.e., if you screw it up, you'll have to edit again (albeit in the same window or tab)

        When done, you may have something like this:


        Initial narrative here. Blah, blah, blah, etc. and so forth....

        for $_(@foobar) { print $foo }

        UPDATE: Copy/paste error in OP (wrong var in first line; no semi-colon in Ln 2); correct code below:

        <c> for my $foo(@foobar) { print $foo; }

        But, again, ya' gotta' register to make this work.

        I was made aware that Anonymous Monk can't edit posts, so because the OP was AnonyMonk, the below doesn't fit for this case. It is relevant for all logged in users however.

        To edit your post, simply click on the title link, and the edit box will be near the bottom of the ensuing page :)

        If you wouldn't mind, could you copy in the explanation points you made in Re^2: Breaking from a foreach loop, returning to position to your original top-level question/post? There are significantly important bits of info, and you'll have a higher probability of getting qualified answers if the top post has all of the info (there's more of a chance of people skipping over your question if it doesn't have enough detail).

Re: Breaking from a foreach loop, returning to position
by kennethk (Abbot) on Aug 26, 2015 at 15:48 UTC
    Based on what you've described here and elsewhere in the thread, your solution could be as easy as:
    our $i_cache; for my $i ((defined $i_cache ? $i_cache+1 : 0) .. $#array) { $i_cache = $i; if(CONDITION_MET){ last; } } undef $i_cache if $i_cache == $#array; # Reset if we went the whole wa +y
    Note I've used our for the index cache, so that it is a global variable and thus will persist. This solution is not thread safe, but could be made so.

    It's worth noting that the follow will not work:

    our $i; for $i ($i ..10) { last if $i == 5; } print $i;
    When you use a variable with a scope outside a loop as the loop variable, is is automatically localized, so it won't give you a hint as to where the loop ended.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      I'm having some trouble implementing your solution. When I had browsed the internet before posting, I came across similar syntax in the for statement, and I think it has to do with my array that is being iterated. When I would print out $_, or in this case, $i, instead of giving me the actual data, I get the index of the array. How would I print out, and use, the actual data in the array? Would using a C-style $array_scalar = $array[$i] work, or is this syntax not supported in Perl?
        When I would print out $_, or in this case, $i, instead of giving me the actual data, I get the index of the array. How would I print out, and use, the actual data in the array? Would using a C-style $array_scalar = $array[$i] work, or is this syntax not supported in Perl?

        Sigh. How hard can it be to type that into your script and TRY IT?

        BTW: The usual way to filter lists (including arrays) is to use grep if you want a list of all matching elements. E.g. to get a list of all even numbers in the range from 1 to 10:

        >perl -E 'say for grep { $_%2==0 } (1..10)' 2 4 6 8 10 >

        (On Windows, use double quotes instead of single quotes.)

        List::Util can do more:

        What is the FIRST even number in that range?

        >perl -E 'use List::Util qw( first ); say first { $_%2==0 } (1..10)' 2 >

        Are there ANY even numbers in that range?

        >perl -E 'use List::Util qw( any ); say any { $_%2==0 } (1..10)' 1 >

        (any() returns a boolean value, so 1 means "yes", not "there is only one even number")

        Are ALL numbers in that range even?

        >perl -E 'use List::Util qw( all ); say all { $_%2==0 } (1..10)' >

        (Again a boolean result, an empty string or undefined value. Both are false, telling you that the range contains numbers that are not even.)

        What's the SUM of all even numbers in that range?

        >perl -E 'use List::Util qw( sum ); say sum grep { $_%2==0 } (1..10)' 30 >

        And so on ...

        Hint: List::MoreUtils knows even more tricks.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

        Given an index $i, you can access the associated entry in an array with $array[$i]. See Arrays in perlintro, and consider picking up a copy of Introduction to Perl, or peruse http://learn.perl.org/books/beginning-perl/ for a free resource.


        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Breaking from a foreach loop, returning to position
by LanX (Saint) on Aug 26, 2015 at 16:39 UTC
    Maybe you just want to call a subroutine? (Which leaves your loop and returns to the same place)

    otherwise you can use goto to jump into the middle of a loop. .. as long as it's not done with foreach.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

Re: Breaking from a foreach loop, returning to position
by Eily (Monsignor) on Aug 26, 2015 at 14:54 UTC

    In Python I would have told you to use a generator, with the yield statement (which allows to interrupt the execution of a function to yield a value, and resume the execution later) :).

    As far as I could tell though, it looks like grep might just do what you want, although it doesn't have the "evaluation-laziness" of yield. grep will allow you to extract the elements that match a condition from a list:

    my @list = 1..10; my @odd = grep { $_ % 2 } @list; # Now you can work on the elements of @odd one element at a time, by r +emoving them as you use them if you want.

Re: Breaking from a foreach loop, returning to position
by anonymized user 468275 (Curate) on Aug 27, 2015 at 15:32 UTC
    I would say that the subroutine call (or method call to a module, depending on ideal code design) as suggested by LanX is almost imperative. While stretching my imagination for possibilities (given the limited information), it would take something monumentally arcane to justify anything else.

    One world, one people

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1140045]
Approved by herveus
Front-paged by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (4)
As of 2024-04-24 18:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found