我有这段代码:
users = ["foo", "bar"]
users.collect { |item, value = []| value << {:name => item} }.flatten
这就像红宝石中的风一样 - 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
Reading the documentation这是真的,collect不期望两个参数,但它在ruby 1.9.2中工作。我错过了什么,我的Array
/ Enumerable
是以某种奇怪的方式修补的,还是文档错了?
答案 0 :(得分:5)
我觉得你错过了什么。这就是你想要做的,它适用于1.9和1.8:
users.collect { |i| { :name => i } }
答案 1 :(得分:2)
1.8.7并没有抱怨阻止获取两个参数,它抱怨你试图为第二个参数提供默认值。这样:
users.collect { |item, value| value << {:name => item} }.flatten
在1.8.7中解析正常,但当然,它在运行时会失败,因为value
是nil
。
1.9允许块参数的默认值(见下文)。
所以不,文档没有错,你只是以一种在1.9.2中工作的奇怪方式使用collect
,因为它允许块参数的默认值。
无论如何,你对collect
的使用有点复杂,可能没有做到你认为它正在做的事情,你应该听Casper并做一个简单的collect
:
users.collect { |item| { :name => item } }
但是,如果你有<<
的东西并想要使用它,无论如何,你可以在1.8.7和1.9.2中使用inject
:
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: ||