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

B

Brian Adkins

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
 
G

Gary Wright

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
 
M

Morton Goldberg

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
 
B

Brian Adkins

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
 
B

Brian Adkins

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
 
A

ara.t.howard

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://codeforpeople.com/
 
H

Harry Kakueki

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
 
M

mortee

Brian said:
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?

I'd try something along these lines:

# sets up a binding with a local variable _iteration = 0, and then
# creates a lambda which inherits this binding, and can use the variable
# note that the lambda also inherits any block given to skip_first,
# so it can also yield

def skip_first(_skip_count = 1)
_iteration = 0
lambda do |x|
yield x if _iteration >= _skip_count
_iteration += 1
end
end

# you can then pass the lambda returned by skip_first to any iterator,
# using the & notation

5.times &skip_first {|a| puts a}
1
2
3
4
 
A

Ari Brown

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.
 
J

James Edward Gray II

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
 
M

Morton Goldberg

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).


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

<code>
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"]
</code>

But it would appear you've already rejected that kind of solution.
Why don't you like it?

Regards, Morton
 
P

Peña, Botp

From: news [mailto:[email protected]] On Behalf Of mortee
# def skip_first(_skip_count =3D 1)
# _iteration =3D 0
# lambda do |x|
# yield x if _iteration >=3D _skip_count
# _iteration +=3D 1
# end
# end
#=20
# # you can then pass the lambda returned by skip_first to any iterator,
# # using the & notation
#=20
# 5.times &skip_first {|a| puts a}
# 1
# 2
# 3
# 4

now that is cool :)
kind regards -botp
 
M

mortee

Peña said:
From: news [mailto:[email protected]] On Behalf Of mortee
# def skip_first(_skip_count = 1)
# _iteration = 0
# lambda do |x|
# yield x if _iteration >= _skip_count
# _iteration += 1
# end
# end
#
# # you can then pass the lambda returned by skip_first to any iterator,
# # using the & notation
#
# 5.times &skip_first {|a| puts a}
# 1
# 2
# 3
# 4

now that is cool :)
kind regards -botp

Thx :)

In the meantime I just realized that the binding of the block which does
the real work (the innermost one) is distinct from the one which does
the counting, so using cryptic variable names trying to avoid name
collisions isn't even needed.

mortee
 
R

Robert Klemme

2007/10/26 said:
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

Of course there is also a solution with #inject. It can be abused when
using this form:

foo.inject do |junk,x|
puts x
end

:)

Other than that you could do

module Enumerable
def skip(n,&b)
raise ArgumentError, "invalid skip count" if n<0
each_with_index {|a,i| b[a] if i>=n}
end
end

This could be extended to skip the last n elements if n is negative
but this is more complex.

Kind regards

robert
 
A

Ari Brown

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

Hm. I didn't know blocks could do that!

So if you wanted to skip the first two, you'd do:

This easily goes on the 1337 wall of 1337. Have never seen something
like this!

-------------------------------------------------------|
~ Ari
crap my sig won't fit
 
B

Brian Adkins

Consider the following code:
...

Thanks for all the feedback. My apologies for confusing many people
with my choice of an example. This should work for any type of
iteration "n.times", "while true", "foo.each", etc., and I don't want
to skip processing the first element of a collection.

Here's the specific example that motivated the question with my
current solution, hopefully it will clarify some things.

class SkipN
def initialize n
@n = n
end

def run
if @n < 1
yield
else
@n -= 1
end
end

def self.skip_first
return new(1)
end
end

skip_first = SkipN.skip_first
ARGV.each do |domain|
skip_first.run { sleep 10 }
available = `whois #{domain}` =~ /No match for "#{domain.upcase}"\./
puts "#{domain} is #{available ? '' : 'NOT'} available"
end

I think it's much clearer to have the block defined within the loop
than to create a lambda outside the loop because this is a replacement
for the following code that would normally be in the loop:

if first
first = false
else
# do some processing
end

Originally, I simply placed the sleep at the end of the loop, but I
didn't like the unnecessary sleep on the last iteration.

Brian Adkins
 
B

Brian Adkins

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).
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

<code>
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"]
</code>

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 }
 
B

Brian Adkins

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 }

Clever :) I've used a similar technique in JavaScript, but I don't
like defining the code outside of the loop, and it's also not as clear
as other solutions IMO.
 
B

Brian Adkins

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.

Correct.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top