开发者

Understanding Ruby Enumerable#map (with more complex blocks)

Let's say I have a function

def odd_or_even n
  if n%2 == 0
    return :even
  else
    return :odd
  end
end

And I had a simple enumerable array

simple = [1,2,3,4,5]

And I ran it through map, with my function, using a do-end block:

simple.map do
  |n| odd_or_even(n)
end
# => [:odd,:even,:odd,:even,:odd]

How could I do this without, say, defining the function in the first place? For example,

# does not work
simple.map do |n|
  if n%2 == 0
    return :even
  else
    return :odd
  end
end

# Desired result:
# => [:odd,:even,:odd,:even,:odd]

is not valid ruby, and the compiler gets mad at me for even thinking about it. But how would I implement an equivalent sort of t开发者_开发百科hing, that works?

edit

In reality, the solution to my problem matters to me a lot less than the motivation/reasoning behind it, to help me understand more how ruby blocks work :)


You're so close. Just remove the returns and you're golden.

This is because the block passed to map is a proc (i.e. created with Proc.new), and not a lambda. A return within a proc doesn't just jump out of the proc- it jumps out of the method that executed (i.e. called call on) the proc. A return within a lambda, on the other hand, jumps out of only the lambda.

The proc method returns a lambda in Ruby 1.8, and a Proc in Ruby 1.9. It's probably best to just not use this method and be explicit with which construct you want to use.

I'm guessing you were either in IRB or a plain ruby script when you were trying this out.

a = Proc.new { return }
a.call # fails. Nothing to return from.

def foobar
  a = Proc.new { return }
  a.call
  puts 'hello' # not reached. The return within the proc causes execution to jump out of the foobar method.
end
foobar # succeeds, but does not print 'hello'. The return within the proc jumps out of the foobar method.

b = lambda { return }
b.call # succeeds. The return only returns from the lambda itself.

def bazquux
  b = lambda { return }
  b.call
  puts 'hello' # this is reached. The lambda only returned from itself.
end
bazquux # succeeds, and prints 'hello'

The lesson to learn from this is to use implicit returns unless you can't, I guess.


I suspect this may be a duplicate question, but to give a value out of a block, use next

simple.map do |n|
  if n%2 == 0
    next :even
  else
    next :odd
  end
end


Shortest variant using Andrew's answer:

simple.map { |n| next :even if n % 2 == 0; :odd }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜