Justin L.
Justin L.

Reputation: 13600

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 thing, 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 :)

Upvotes: 5

Views: 4027

Answers (3)

Bubonic Pestilence
Bubonic Pestilence

Reputation: 107

Shortest variant using Andrew's answer:

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

Upvotes: 2

Andrew Grimm
Andrew Grimm

Reputation: 81510

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

Upvotes: 9

x1a4
x1a4

Reputation: 19485

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.

Upvotes: 13

Related Questions