开发者

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: ||
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜