Перечислитель: метод сбора с двумя параметрами

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

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 каким-то странным образом исправляются или документация неверна?


person juandebravo    schedule 04.08.2011    source источник
comment
В карте/коллекции вы никогда не делаете обновление на месте (побочный эффект), это первый принцип функционального программирования (отсюда и карта)   -  person tokland    schedule 04.08.2011


Ответы (2)


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

users.collect { |i| { :name => i } }
person Casper    schedule 04.08.2011
comment
+1 collect уже неявно выполняет итеративное добавление к логике пустого начального списка, которую вы пытаетесь сделать самостоятельно, - это (а) причина, по которой он существует в первую очередь; (б) вот почему у него такое имя: собираются результаты каждой оценки блока. - person Karl Knechtel; 04.08.2011

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

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

отлично анализируется в 1.8.7, но, конечно, во время выполнения он падает, потому что value это nil.

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

Так что нет, в документации нет ошибок, вы просто используете collect< /a> странным образом, который работает в 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: ||
person mu is too short    schedule 04.08.2011