Понимание Ruby Enumerable#map (с более сложными блоками)

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

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, и компилятор злится на меня за то, что я даже подумал об этом. Но как мне реализовать эквивалентную вещь, которая работает?

изменить

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


person Justin L.    schedule 15.06.2010    source источник
comment
Для чего это стоит, вы можете сделать 1.даже? или 1. нечетный? в рубине ›= 1,8,7   -  person steenslag    schedule 15.06.2010


Ответы (3)


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

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

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

Я предполагаю, что вы были либо в IRB, либо в простом скрипте ruby, когда пробовали это.

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'

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

person x1a4    schedule 15.06.2010
comment
Однако есть ли причина, почему это происходит? Блок просто выводит последнюю выполненную команду, как при неявном возврате? Я спрашиваю об этом, потому что хотел бы иметь возможность предсказать, что произойдет; то, что они вышли, кажется немного... случайным. - person Justin L.; 15.06.2010
comment
Короткая версия - это одно из различий между процедурами и лямбда-выражениями. Работа над лучшим объяснением. И да, блоки просто вернут в них самое последнее вычисленное выражение. - person x1a4; 15.06.2010
comment
Спасибо за объяснение; это очень подробно и полезно =) только один последний вопрос... можно ли передать лямбду как блок? на #map, возможно? - person Justin L.; 15.06.2010
comment
В некоторых случаях вы можете использовать next. - person Andrew Grimm; 15.06.2010
comment
Да, вы можете передать лямбду как блок, но в этом случае она действует как процедура (т. е. возврат внутри нее невозможен). add_one = lambda { |n| n + 1 }; [1, 2, 3].map &add_one # [2, 3, 4] - person x1a4; 15.06.2010

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

simple.map do |n|
  if n%2 == 0
    next :even
  else
    next :odd
  end
end
person Andrew Grimm    schedule 15.06.2010
comment
хм, кажется, это именно то, что я искал :) спасибо :) - person Justin L.; 15.06.2010

Самый короткий вариант с использованием ответа Эндрю:

simple.map { |n| next :even if n % 2 == 0; :odd }
person Bubonic Pestilence    schedule 05.05.2012
comment
Сделав еще один шаг вперед: simple.map {|n| next n%2 == 0 ? :even : :odd } ;-) - person Prakash Murthy; 07.10.2012