From: Brian Adkins Date: 2007-10-26T22:10:01+09:00 Subject: Re: Skip the first invocation e.g. skip_first { foo } On Oct 25, 11:24 pm, Morton Goldberg wrote: > 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? I think it's the least objectionable at this point :) It's just not as nice as: skip_first { foo }