From: Morton Goldberg Date: 2007-10-26T12:24:27+09:00 Subject: Re: Skip the first invocation e.g. skip_first { foo } On Oct 25, 2007, at 7:30 PM, Brian Adkins wrote: > On Oct 25, 7:18 pm, Morton Goldberg wrote: >> On Oct 25, 2007, at 6:40 PM, Brian Adkins wrote: >> >> >> >>> Consider the following code: >> >>> first = true >>> 3.times do >>> if first >>> first = false >>> else >>> puts 'foo' >>> end >>> ... >>> end >> >>> I'd like to be able to do the following instead: >> >>> 3.times do >>> skip_first { puts 'foo' } >>> ... >>> end >> >>> However, I'm pretty sure that's impossible - especially when you >>> consider running the above code twice would require state to be >>> initialized twice, so I expect some initialization outside the >>> loop is >>> necessary. >> >>> So, what's the most elegant way to solve this? >> >>> Here are a couple I've come up with minus some implementation >>> details. >>> They work, but I'm not very pleased with either one. I don't recall >>> ever needing 'n' to be other than 1, but it feels strange to not >>> generalize it. I also realize it's more typical to want to execute >>> code only on the first loop invocation, but I had the opposite need >>> when this question arose. >> >>> # Use an object for state >>> skip_first = SkipN.new(1) >>> 3.times do >>> skip_first.run { puts 'hi' } >>> ... >>> end >> >>> # Use a closure for state >>> skip_first = skipn(1) >>> 3.times do >>> skip_first.call lambda { puts 'hi' } >>> ... >>> end >> >>> I experimented with using a binding, but I discovered that a new >>> binding is created each time times invokes the block, so apparently >>> it's not possible to introduce a variable within the lexical >>> scope of >>> the block for the duration of the 3.times invocations - or I missed >>> something. >> >> It's not entirely clear to me what you are trying to accomplish, so >> this may way off base. > > What I'm trying to accomplish is a convenient way to execute a block > of code on every iteration except the first (or only on the first > iteration). > >> For some reason you are ignoring that Integer#times passes an index >> into its block, > > Yes, that's because a loop index will not always be available. The > times example was just an example. Consider: > > foo.each do |bar| > skip_first { ... } > ... > end Well, I'm pretty simple minded, so I would use a simple class to solve this problem. Something like class Skipper def initialize(n) @n = n end def call yield if (@n -= 1) < 0 end end skip_1 = Skipper.new(1) a = [] 4.times { |i| skip_1.call { a << i } } a # => [1, 2, 3] skip_2 = Skipper.new(2) a = [] %w[a b c d].each { |w| skip_2.call { a << w } } a # => ["c", "d"] But it would appear you've already rejected that kind of solution. Why don't you like it? Regards, Morton