[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Skip the first invocation e.g. skip_first { foo }

Brian Adkins

10/25/2007 10:39:00 PM

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.

Brian Adkins

30 Answers

Gary Wright

10/25/2007 10:50:00 PM

0

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
>

3.times do |i|
print '2nd or more: ' unless i.zero?
puts "iteration: #{i}"
end

Gary Wright


Morton Goldberg

10/25/2007 11:18:00 PM

0

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.

For some reason you are ignoring that Integer#times passes an index
into its block, but if are willing to make use of this index, what
you propose can be written as

3.times { |i| puts "hi" if i > 0 }

Regards, Morton



Brian Adkins

10/25/2007 11:26:00 PM

0

On Oct 25, 6:50 pm, Gary Wright <gwtm...@mac.com> 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
>
> 3.times do |i|
> print '2nd or more: ' unless i.zero?
> puts "iteration: #{i}"
> end

I guess I wasn't clear enough. This should work in any type of
iteration, so a loop index may not be available. For example:

foo.each do |bar|
skip_first { ... }
...
end

Brian Adkins

10/25/2007 11:29:00 PM

0

On Oct 25, 7:18 pm, Morton Goldberg <m_goldb...@ameritech.net> 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



ara.t.howard

10/25/2007 11:35:00 PM

0


On Oct 25, 2007, at 4:40 PM, Brian Adkins wrote:

> first = true
> 3.times do
> if first
> first = false
> else
> puts 'foo'
> end
> ...
> end

as others have mentioned you can use the block counter that's passed
- but in general i've done things like this before:

cfp:~ > cat a.rb
block = lambda{ block = lambda{ puts 'foo' } }
3.times{ block.call }


cfp:~ > ruby a.rb
foo
foo


which, of course, can work for things like #each too.

a @ http://codeforp...
--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama




Harry Kakueki

10/26/2007 12:32:00 AM

0

On 10/26/07, Brian Adkins <lojicdotcom@gmail.com> 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
>


arr = %w[foo bar bat foo]

arr[1..-1].each do |x|
p x
end

puts
# OR

arr.each_with_index do |x,i|
p x if i !=0
end

If these are no good, then I am not understanding your question.

Harry

--
A Look into Japanese Ruby List in English
http://www.ka...

mortee

10/26/2007 2:28:00 AM

0

ara.t.howard

10/26/2007 2:49:00 AM

0


On Oct 25, 2007, at 8:28 PM, mortee wrote:

> 5.times &skip_first {|a| puts a}

that makes for a quite interesting syntax option if you chained a few
levels deep too:

foo &a &b &c { bar }

interesting stuff!

a @ http://codeforp...
--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama




Ari Brown

10/26/2007 2:57:00 AM

0


On Oct 25, 2007, at 7:35 PM, ara.t.howard wrote:

> as others have mentioned you can use the block counter that's
> passed - but in general i've done things like this before:
>
> cfp:~ > cat a.rb
> block = lambda{ block = lambda{ puts 'foo' } }
> 3.times{ block.call }
>
>
> cfp:~ > ruby a.rb
> foo
> foo

Whoa! That's pretty intense! Can you please explain how that works?
I'm totally lost to your magic.


Ari
--------------------------------------------|
If you're not living on the edge,
then you're just wasting space.



James Gray

10/26/2007 3:03:00 AM

0

On Oct 25, 2007, at 9:56 PM, Ari Brown wrote:

>
> On Oct 25, 2007, at 7:35 PM, ara.t.howard wrote:
>
>> as others have mentioned you can use the block counter that's
>> passed - but in general i've done things like this before:
>>
>> cfp:~ > cat a.rb
>> block = lambda{ block = lambda{ puts 'foo' } }
>> 3.times{ block.call }
>>
>>
>> cfp:~ > ruby a.rb
>> foo
>> foo
>
> Whoa! That's pretty intense! Can you please explain how that works?
> I'm totally lost to your magic.

It changes what the local variable block holds on the first call. So
basically, the first call is the warm-up call that builds a function
to handle all future calls which do the real work.

James Edward Gray II