views:

85

answers:

2

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

+2  A: 

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.

x1a4
Is there any reason *why* this happens, though? Does the block simply put out the last-executed-command, like in implicit returns?I ask this because I'd like to be able to predict what happens; having these come out just seems a bit...random.
Justin L.
The short version is "it's one of the differences between procs and lambdas." Working on a better explanation. And yes, blocks will just return the most recently evaluated expression in them.
x1a4
thanks for the explanation; it's very thorough and helpful =)just one last question...is it possible to pass a lambda as a block? to #map, perhaps?
Justin L.
You may want to use `next` under some circumstances.
Andrew Grimm
x1a4
+1  A: 

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
Andrew Grimm
hm, this seems to be exactly what I was looking for :) thanks :)
Justin L.