Понимание Ruby Enumerable # map (с более сложными блоками) - PullRequest
5 голосов
/ 15 июня 2010

Допустим, у меня есть функция

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

А у меня был простой перечислимый массив

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

И я проверил его по карте с моей функцией, используя блок do-end:

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

Как я мог бы сделать это без, скажем, определения функции в первую очередь? Например,

# 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]

не является допустимым ruby, и компилятор злится на меня за то, что я даже думаю об этом. Но как бы я реализовал эквивалентную вещь, которая работает?

редактировать

На самом деле, решение моей проблемы имеет для меня гораздо меньшее значение, чем мотивация / обоснование, чтобы помочь мне понять, как работают блоки ruby

Ответы [ 3 ]

13 голосов
/ 15 июня 2010

Ты так близко. Просто удалите return s, и вы получите золотой.

Это потому, что блок, переданный в map, является процедурой (т. Е. Созданной с помощью Proc.new), а не лямбда-выражением. return внутри процесса не просто выпрыгивает из процесса, он выпрыгивает из метода, который выполнял (то есть вызывал call вкл) процесс. С другой стороны, возврат в лямбду выпрыгивает только из лямбды.

Метод proc возвращает лямбду в Ruby 1.8 и Proc в Ruby 1.9. Вероятно, лучше просто не использовать этот метод и четко указывать, какую конструкцию вы хотите использовать.

Полагаю, вы были либо в IRB, либо в простом рубиновом скрипте, когда пытались это сделать.

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'

Урок, который можно извлечь из этого, заключается в использовании неявных возвратов, если вы не можете, я думаю.

9 голосов
/ 15 июня 2010

Я подозреваю, что это может быть повторяющийся вопрос, но чтобы указать значение вне блока, используйте next

simple.map do |n|
  if n%2 == 0
    next :even
  else
    next :odd
  end
end
3 голосов
/ 06 мая 2012

Кратчайший вариант ответа Андрея:

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