Enumerator: collect method with two parameters
I have this code:
users = ["foo", "bar"]
users.collect { |item, value = []| value << {:name => item} }.flatten
That is wor开发者_开发知识库king like the wind in ruby-1.9.2:
=> [{:name=>"foo"}, {:name=>"bar"}]
But this does not work in ruby-1.8.7 because it does not like collect getting two parameters:
SyntaxError: compile error
(irb):2: syntax error, unexpected '=', expecting '|'
users.collect { |item, value = []| value << {:name => item} }.flatten
Reading the documentation that's true, collect does not expect two parameters, but it's working in ruby 1.9.2. So am I missing something, my Array
/Enumerable
is being patched in some weird way or is the documentation wrong?
I think you are missing something. This is what you want to do, and it works in both 1.9 and 1.8:
users.collect { |i| { :name => i } }
1.8.7 isn't complaining about the block getting two parameters, it is complaining about your attempt to provide a default value for the second parameter. This:
users.collect { |item, value| value << {:name => item} }.flatten
parses fine in 1.8.7 but, of course, it falls over at run time because value
is nil
.
1.9 does allow default values for block arguments (see below).
So no, the documentation isn't wrong, you're just using collect
in a strange way that works in 1.9.2 because it allows default values for block arguments.
Anyway, your using of collect
is a bit convoluted and might not be doing quite what you think it is doing, you should listen to Casper and just do a simple collect
:
users.collect { |item| { :name => item } }
But, if you have a thing for <<
and want to use it no matter what, you could use inject
in both 1.8.7 and 1.9.2:
users.inject([ ]) { |value, item| value << { :name => item } }
That is pointless complexity though.
You piqued my curiosity so I went to the Ruby parser files for an authoritative reference. Pointless busy work perhaps but "pointless" and "bad" are different things.
The 1.9.2-p180 parse.y
has these things:
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
If you trace through it a bit, you'll see that the block_param
rule is used for things like this:
{ |eggs| ... }
{ |two_cent, stamp| ... }
{ |where_is, pancakes = 'house'| ... }
and the do
/end
form as well. Then trace from block_param
down to f_block_opt
and you'll see where default values are explicitly allowed by the grammar.
OTOH, The 1.8.7-p248 parse.y
has this:
opt_block_var : none
| '|' /* none */ '|'
| tOROP
| '|' block_var '|'
There is nothing in block_var
that allows default values for block arguments. The tOROP
is just there to allow both of these forms:
{ | | pancakes } # '|' /* none */ '|'
{ || pancakes } # tOROP, the logical "or" operator: ||
精彩评论