Перечислитель: метод сбора с двумя параметрами - PullRequest
1 голос
/ 04 августа 2011

У меня есть этот код:

users = ["foo", "bar"]
users.collect { |item, value = []| value << {:name => item} }.flatten

Это работает как ветер в ruby-1.9.2:

=> [{:name=>"foo"}, {:name=>"bar"}]

Но это не работает в ruby-1.8.7, потому что он не любит собирать, получая два параметра:

SyntaxError: compile error
(irb):2: syntax error, unexpected '=', expecting '|'
users.collect { |item, value = []| value << {:name => item} }.flatten

Чтение документации это правда, collect не ожидает двух параметров, но работает в ruby ​​1.9.2. Так что я что-то упустил, мой Array / Enumerable исправлен каким-то странным образом или документация неверна?

Ответы [ 2 ]

5 голосов
/ 04 августа 2011

Я думаю, что вы что-то упустили. Это то, что вы хотите сделать, и это работает в 1.9 и 1.8:

users.collect { |i| { :name => i } }
2 голосов
/ 04 августа 2011

1.8.7 не жалуется, что блок получает два параметра, а жалуется на вашу попытку указать значение по умолчанию для второго параметра.Это:

users.collect { |item, value| value << {:name => item} }.flatten

хорошо разбирает в 1.8.7, но, конечно, он падает во время выполнения, потому что value is nil.

1.9 разрешает значения по умолчанию для блокааргументы (см. ниже).

Так что нет, документация не ошибается, вы просто используете collect странным образом, который работает в 1.9.2, потому что он позволяет использовать значение по умолчаниюзначения для аргументов блока.

В любом случае, использование collect немного запутанно и может не совсем соответствовать тому, о чем вы думаете, вы должны послушать Каспера и просто сделать collect:

users.collect { |item| { :name => item } }

Но, если у вас есть вещь для << и вы хотите использовать ее, несмотря ни на что, вы можете использовать inject в 1.8.7 и 1.9.2:

users.inject([ ]) { |value, item| value << { :name => item } }

Это бессмысленная сложность.


Вы пробудили мое любопытство, поэтому я обратился к файлам анализатора Ruby за авторитетным справочником.Возможно, бессмысленная занятая работа, но «бессмысленная» и «плохая» - это разные вещи.

1.9.2-p180 parse.y имеет следующие вещи:

block_param     : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
                | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
                | f_arg ',' f_block_optarg opt_f_block_arg
                /* ... */
f_block_optarg  : f_block_opt
f_block_opt     : tIDENTIFIER '=' primary_value

Если вы проследите егонемного, вы увидите, что правило block_param используется для таких вещей, как:

{ |eggs| ... }
{ |two_cent, stamp| ... }
{ |where_is, pancakes = 'house'| ... }

и форма do / end.Затем проследите от block_param до f_block_opt, и вы увидите, где значения по умолчанию явно разрешены грамматикой.

OTOH, 1.8.7-p248 parse.y имеет это:

opt_block_var : none
              | '|' /* none */ '|'
              | tOROP
              | '|' block_var '|'

В block_var нет ничего, что позволяло бы использовать значения по умолчанию для аргументов блока.tOROP только для того, чтобы разрешить обе эти формы:

{ | | pancakes }    # '|' /* none */ '|'
{ ||  pancakes }    # tOROP, the logical "or" operator: ||
...