We've been having this discussion about defining your own while loop in a thread about Haskell and I thought I'd change the subject in case people who had no interest in Haskell were avoiding it ;-) In article <d26sfn01vqs / enews3.newsguy.com>, Phil Tomson <ptkwt / aracnet.com> wrote: >In article <3anjpeF6d91alU1 / individual.net>, >Robert Klemme <bob.news / gmx.net> wrote: >> >>"Phil Tomson" <ptkwt / aracnet.com> schrieb im Newsbeitrag >>news:d24b8d02fq1 / enews4.newsguy.com... >>> In article <3alo9bF6d2vo8U1 / individual.net>, >>> Robert Klemme <bob.news / gmx.net> wrote: >>>> >>>> >>>>I'm surprised about "retry", too. His analysis sounds all very >>>>resonable - >>>>only that "break" does not work for me but "return" does: >>>> >>>>>> def my_while(cond) >>>>>> break unless cond >>>>>> yield >>>>>> retry >>>>>> end >>>>=> nil >>>>>> i = 0 >>>>=> 0 >>>>>> my_while i < 10 do >>>>?> puts i >>>>>> i += 1 >>>>>> end >>>>0 >>>>1 >>>>2 >>>>3 >>>>4 >>>>5 >>>>6 >>>>7 >>>>8 >>>>9 >>>>LocalJumpError: unexpected break >>>> from (irb):2:in `my_while' >>>> from (irb):7 >>> >>> >>> Hmmm... break works find for me in 1.8.2. Time to upgrade? >> >>Yeah, maybe. >> >>>>Btw, can anybody think of a way to make my_while return the result of the >>>>last block evaluation? It seems impossible because the return occurs >>>>before >>>>the yield... >>>> >>> >>> >>> How about: >>> >>> def my_while(cond) >>> return @ret unless cond >>> @ret = yield >>> retry >>> end >> >>Not thread safe. And I think also not nesting safe. >> > >Yes, I know. Did you see my later post? I don't think it made it from >the newsgroup to the mailing list. > >Here's the code to make it nestable and threadsafe: > > class Whiler > def initialize > @ret = nil > end > > def while(cond) > break @ret unless cond > @ret = yield > retry > end > end > > outer = Whiler.new > inner = Whiler.new > > i=0 > outer.while i<5 do > puts "i: #{i}" > j=0 > inner.while j<5 do > puts " j: #{j}" > j+=1 > end > i+=1 > end > >A bit cumbersome, perhaps, but it is definately nestable and should be >threadsafe as well. > Actually, I figured out a way to make it much more natural: Change Whiler to accept an optional condition in it's constructor like so: class Whiler def initialize(cond=nil) @ret = nil @cond = cond end def while(cond=@cond) break @ret unless cond @ret = yield retry end end Define the my_while top-level method like so: def my_while(cond,&block) Whiler.new(cond).method(:while).call(&block) end Now the user of my_while doesn't need to know that a Whiler class even exists. Usage example: i = 0 my_while(i<10) do puts i j=0 my_while(j<10) do puts " j: #{j}" j+=1 end i+=1 end I think there are some implications for DSLs (Domain Specific Languages) in this example. The my_while method as defined above (the last one) hides some Ruby language details from the user of my_while so that my_while can appear like a natural looping construct. I've created DSLs for people who had no idea they were using Ruby underneath; this sort of thing is necessary so they don't feel as though they are obliged to learn Ruby to use the DSL. Another example of why Ruby is so good for creating DSLs. ;-) Phil