1

我有这个代码:

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

这就像 ruby​​-1.9.2 中的风一样工作:

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

但这在 ruby​​-1.8.7 中不起作用,因为它不喜欢 collect 获取两个参数:

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

阅读正确的文档,collect 不需要两个参数,但它在 ruby​​ 1.9.2 中工作。那么我是否遗漏了什么,我的Array/Enumerable正在以某种奇怪的方式进行修补,或者文档是否错误?

4

2 回答 2

5

我认为你错过了一些东西。这是您想要做的,它在 1.9 和 1.8 中都有效:

users.collect { |i| { :name => i } }
于 2011-08-04T07:31:45.290 回答
2

1.8.7 并没有抱怨块获取两个参数,而是抱怨您尝试为第二个参数提供默认值。这:

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

在 1.8.7 中解析得很好,但是当然,它在运行时会崩溃,因为valueis nil.

1.9 确实允许块参数的默认值(见下文)。

所以不,文档没有错,你只是collect在以一种奇怪的方式使用 1.9.2,因为它允许块参数的默认值。

无论如何,你的使用collect有点复杂,可能没有做你认为它正在做的事情,你应该听 Casper 并做一个简单的collect

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

但是,如果你有一个东西<<并且无论如何都想使用它,你可以inject在 1.8.7 和 1.9.2 中使用:

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

不过,那是毫无意义的复杂性。


你激起了我的好奇心,所以我去 Ruby 解析器文件寻求权威参考。忙碌的工作也许毫无意义,但“无意义”和“糟糕”是不同的东西。

1.9.2-p180parse.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-p248parse.y有这个:

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

没有任何block_var内容允许块参数的默认值。tOROP只是允许这两种形式:

{ | | pancakes }    # '|' /* none */ '|'
{ ||  pancakes }    # tOROP, the logical "or" operator: ||
于 2011-08-04T07:36:42.467 回答