Несоответствие арности между Hash.each и лямбдами - PullRequest
3 голосов
/ 10 марта 2011

Я поднял следующий пример из Джош Сассер

  def strip_accents params
    thunk = lambda do |key,value|
      case value
        when String then value.remove_accents!
        when Hash   then value.each(&thunk)
      end
    end
    params.each(&thunk)
  end

когда я помещаю его в консоль rails (irb) и вызываю его с помощью хэша, я получаю следующее:

ruby-1.9.2-p136 :044 > `ruby --version`
 => "ruby 1.9.2p136 (2010-12-25 revision 30365) [i686-linux]\n"
ruby-1.9.2-p136 :045 > strip_accents({:packs=>{:qty=>1}})
ArgumentError: wrong number of arguments (1 for 2)
        from (irb):32:in `block in strip_accents'
        from (irb):37:in `each'
        from (irb):37:in `strip_accents'
        from (irb):45
        from /longpathtrimedforclarity/console.rb:44:in `start'
        from /longpathtrimedforclarity/console.rb:8:in `start'
        from /longpathtrimedforclarity/commands.rb:23:in `<top (required)>'
        from script/rails:6:in `require'
        from script/rails:6:in `<main>'

Я понимаю, что лямбды проверяют арность, но я вижу два аргумента в определении лямбды. Если я изменю lambda do на Proc.new do, код выполнится, и я получу ожидаемый результат.

Пример Джоша из 2008 года, поэтому я предполагаю, что это разница в Ruby 1.8 и 1.9. Что здесь происходит?

Ответы [ 2 ]

3 голосов
/ 10 марта 2011

Действительно, кажется, что он изменился между 1,8 и 1,9, но это изменение исправляет его для 1.9.2, по крайней мере, в моих тестах:

def strip_accents params
  thunk = lambda do |h|
    key, value = h
    case value
    when String then value.remove_accents!
    when Hash   then value.each(&thunk)
    end
  end
  params.each(&thunk)
end

Этот подход оказывается обратно совместимым сРуби 1.8.7, а также.

1 голос
/ 11 марта 2011

Hash#each, как и любой другой метод #each, возвращает один аргумент для блока. В случае Hash#each этот один аргумент является двухэлементным массивом, состоящим из ключа и значения.

Итак, Hash#each дает один аргумент, но ваша лямбда имеет два обязательных параметра, поэтому вы получаете ошибку arity.

Он работает с блоками, поскольку блоки менее строги в отношении своих аргументов, и, в частности, если блок имеет несколько параметров, но получает только один аргумент, он попытается деконструировать аргумент, как если бы он был передан с знак.

Существует два вида Proc с: лямбды и не-лямбды (сбивающие с толку, обычно их также называют Proc с). Лямбды ведут себя как методы с точки зрения поведения ключевого слова return и (что более важно для этого случая) того, как они связывают аргументы, тогда как не-лямбда Proc ведут себя как блоки с точки зрения того, как return и привязка аргументов работают , Вот почему Proc.new (который создает не-лямбду Proc) работает, а lambda (который явно создает лямбду) - нет.

Вы можете проверить, является ли Proc лямбда или нет, позвонив по номеру Proc#lambda?.

Если вы хотите деконструировать аргумент, вам придется делать это явно, так же, как при определении метода:

lambda do |(key, value)|

И, да, более разумный подход к привязке аргументов для блоков, Proc s и лямбда-выражений был одним из основных обратно несовместимых изменений в Ruby 1.9.

...