From: Joel VanderWerf Date: 2006-02-08T00:16:20+09:00 Subject: Re: lazy evaluation? Martin DeMello wrote: > Could someone explain why this code works: > > def repeat(condition) > puts "condition: #{condition}" > yield > retry if not condition > end > > j=0 > repeat (j >= 10) do > puts j > j+=1 > end > > puts "after loop, j = #{j}" > > I'd have expected repeat (j >= 10) to pass "false" into the method, > which would then yield repeatedly to the do/end block, never seeing the > (condition) part again. > > martin What confused me about this is that retry has two meanings, one in the context of a loop or block, and another in the context of begin...end (inside a rescue clause). From PickAxe v2: > break terminates the immediately enclosing loop--control resumes at > the statement following the block. redo repeats the loop from the > start, but without reevaluating the condition or fetching the next > element (in an iterator). The next keyword skips to the end of the > loop, effectively starting the next iteration. retry restarts the > loop, reevaluating the condition. [p.330] ^^^^^^^^^^^^^^^^^^^^^^^^^^ Apparently, this includes the arguments of the method, in the case of an iterator method called with a block. > The retry statement can be used within a rescue clause to restart the > enclosing begin/end block from the beginning. [p. 347] Which interpretation is used can depend on whether a block is present: $ cat thrice.rb def thrice(x) @count = 0 if !@count || @count >= 3 @count += 1 retry if @count < 3 end thrice puts("foo") do end thrice puts("foo") $ ruby thrice.rb foo foo foo foo thrice.rb:4:in `thrice': retry outside of rescue clause (LocalJumpError) from thrice.rb:8 So there's no need to worry about object.foo(ary.pop) reevaluating its argument. That can only happen if a block is present, in which case the caller should be aware of any "control structure" semantics that #foo might have. -- vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407