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

I'm making a crossword puzzle generator, and I'm having trouble figuring out how to make the iterator for it. The code is a bit intense, but what I need to be able to do is this:
my $puzzler = Crossword->new(...); while (my $p = $puzzler->generate) { # do stuff with one possible puzzle }
Now, there's a lot that goes into generating the crossword puzzle, and what I really need is the ability to STOP a function in its tracks, and then resume it. This isn't possible as far as I know, so I'm kind of at a loss for how I could do this.

Has anyone else been in this situation? How have you solved it?

_____________________________________________________
Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Replies are listed 'Best First'.
Re: complex iterator needed (goto)
by tye (Sage) on Feb 01, 2004 at 02:28 UTC

    Move all of your lexicals into your object and write code like:

    sub generate { my( $self )= @_; goto $self->{GOTO} if $self->{GOTO}; ... $self->{GOTO}= 'gnarfle'; return ...; gnarfle: ... }

    - tye        

      cooooooooooooool

      Why do the simplest methods always evade me?

      --
      Tommy Butler, a.k.a. TOMMY
      
        Why do the simplest methods always evade me?

        Cause you're a pinball wizard?

Re: complex iterator needed
by Zaxo (Archbishop) on Feb 01, 2004 at 02:18 UTC

    It sounds exactly like you want coroutines or continuations. This might be a good opportunity to play around with Coro, which includes Coro::Cont, an implementation of continuations by way of coroutines.

    Other approaches might try recursion or threads.

    After Compline,
    Zaxo

Re: complex iterator needed
by neuroball (Pilgrim) on Feb 01, 2004 at 01:50 UTC

    That depends... what would you like to do during the function is stopped? Would you just like to give another program to give a chance to get some CPU time? Or would you like to change some of the data that the function is working on?

    A. There is not really an easy solution... If you just want to give some other programs some CPU time you could trigger the sleep function every so often.

    B. There are two hard solutions... and both include the building of a framework or module.

    The first way to make this possible is to have a global variable that is read by the function. Once the variable signals to stop, the function saves all it's data and states and saves them either in memory or to a file. The memory address of the data or the filename are returned once the function is done with. When you want to resume you just point your function to the already processed data and it's states and let it start from there

    The second version would be the same idea as above but with a more professional interface: Signals. You would write a module/function with signal traps. If the stop signal is given all data and states are saved. If the resume signal is given all saved data and states are reloaded and the function starts where it stopped.

    Summary: It seems that you are in for some development time. You might want to take a look at Thread::Suspend.

    /oliver/

      You might want to take a look at Thread::Suspend.

      Please note that Thread::Suspend only works on systems on which Thread::Signal works. And that means fewer and fewer Linux systems. ;-(

      I'd welcome any suggestions on getting Thread::Signal to reliably work on more platforms.

      Liz

Re: complex iterator needed
by Tommy (Chaplain) on Feb 01, 2004 at 02:09 UTC

    I don't know if this is gonna help much, but we'll see. If you need to be able to stop and resume functions, create your functions as the loops themselves. I've figured it out in my head, but that's the best I can word it. Maybe some pseudo code would help communicate my meaning...

    Instead of using a model like the following...

    # USING LOOPS (not gonna work right) my(@directives) = qw/ up down left right left down up right down left up right up down up down down up left left right right right right right left up down up up up up down down left down left up up down down left left left left left left left right up up down right left down down left right right right right right right left left left down up down up down up down up down down right right down up up up up up up up left left right up right left right left right left up down up down right left left left left right down up left right up up /; # "go up, go down, go right, left, etc... while (@directives) { go('^') while &where eq 'up'; go('v') while &where eq 'down'; go('<') while &where eq 'left'; go('>') while &where eq 'right'; } sub where { shift @directives } sub go { print @_ }

    ...Use a model like this...

    # USING SUBS TO EMULATE LOOP ITERATIONS my(@directives) = qw/ up down left right left down up right down left up right up down up down down up left left right right right right right left up down up up up up down down left down left up up down down left left left left left left left right up up down right left down down left right right right right right right left left left down up down up down up down up down down right right down up up up up up up up left left right up right left right left right left up down up down right left left left left right down up left right up up /; my($directions) = { 'up' => sub { '^' }, 'down' => sub { 'v' }, 'left' => sub { '<' }, 'right' => sub { '>' } }; sub go { while (@_) { print &{ $directions->{ shift @_ } } } } go(@directives); # "go up, go down, go right, left, etc...
    --
    Tommy Butler, a.k.a. TOMMY
    
Re: complex iterator needed
by oakbox (Chaplain) on Feb 01, 2004 at 01:52 UTC
    In cases like this, I usually just shuffle those big constants into a complicated hash reference and stick it on the hard drive with Storable. If you are clever with your file naming, it's easy to have a method determine 'where was I last time?' and load up the appropriate variables to continue processing.
Re: complex iterator needed
by BrowserUk (Patriarch) on Feb 01, 2004 at 11:41 UTC

    Co-routines are almost trivial with threads.

    #! perl -slw use strict; use threads qw[ yield ]; use threads::shared; use Thread::Queue; my $Q = Thread::Queue->new; sub complexGenerator{ my( $max ) = @_; for( 1 .. $max ){ # Do our complex generation here # wait till they are ready for the next result select undef, undef, undef, 0.01 and yield while $Q->pending; # and let them have it $Q->enqueue( $_ ); } # Indicate we're done $Q->enqueue( undef ); } my $coro = threads->new( \&complexGenerator, 100 ); while( my $next = $Q->dequeue ) { # Process latest result set printf $next; <stdin>; } $coro->join; __END__

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    Timing (and a little luck) are everything!

      I thought "yield" was something I might want. DAMN YOU, Ruby! DAMN YOU!
      _____________________________________________________
      Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
      s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

        It works almost as well without the yield.

        That is really a micro-optimisation, in that it relinguishes the rest of the current timeslice, giving the co-routine a chance of getting control a few cycles earlier.

        If your platform supports a sleep that handles microseconds *and* relinguishes timeslice (e.g Win32::Sleep), then the yield becomes completely redundant.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        Timing (and a little luck) are everything!

Re: complex iterator needed
by tilly (Archbishop) on Feb 01, 2004 at 10:51 UTC
    Dominus is in the process of writing a book on this. :-)

    The idea is simple. For every control structure that you would like to use (eg loops), write a function allowing you to produce a parallel iterator control structure that is restartable. After you do that, you can write your function this style, and it is automatically an iterator.

    For an illustration of the idea you can see me doing something similar (albeit on a non-ambitious scale) at Laziness in a more consistent way.

Re: complex iterator needed
by artist (Parson) on Feb 01, 2004 at 01:35 UTC
    May be I didn't understand your question completely. So please rephrase if you think answer is not in the right direction:

    Solution 1:Divide your function in 2 parts and call the second part when necessary. OR 2. Pass different value to the function and function can start at the point where you want.

    artist