理解`array.map(&:method)`

时间:2016-07-07 03:33:13

标签: ruby symbols

为什么:

[1,2,3,4,5].map(&:to_s) #=> ["1", "2", "3", "4", "5"]

工作但是:

[1,2,3,4,5].map(&:*(2))

会引发意外的语法错误吗?

2 个答案:

答案 0 :(得分:3)

&被称为to_proc运算符。它在其后面的表达式上调用to_proc方法,然后将生成的Proc作为块传递给方法。

如果是&:to_s:to_s是一个符号,那么运营商就会调用Symbol#to_proc。文档有点乱码,但只要说这两个表达式或多或少等同就足够了:

my_proc = :to_s.to_proc
my_proc = Proc.new {|obj| obj.to_s }

所以问题的回答“为什么不&:*(2)起作用?”是&运算符:*(2)之后的表达式不是有效的Ruby表达式。它使Ruby解析器与"hello"(2)一样有意义。

顺便说一下,有一种方法可以做你想做的事情:

[1,2,3,4,5].map(&2.method(:*))
# => [2, 4, 6, 8, 10]

在上面的代码中,2.method(:*)将对象*的{​​{1}}方法的引用作为Method对象返回。方法对象的行为与Proc对象很相似,它们响应2。但是,上述内容并不完全相同 - 它只是to_proc而不是2 * n(如果n * 2也是一个数字,则无关紧要) - 并且它不再简洁或者比n可读,因此很少值得这么麻烦。

答案 1 :(得分:-1)

&符号和对象(&:方法)

& operator也可用于将对象作为块传递给方法,如下例所示:

arr = [ 1, 2, 3, 4, 5 ]

arr.map { |n| n.to_s }
arr.map &:to_s

上述两个例子都有相同的结果。在两者中,map方法获取arr数组和一个块,然后它在数组的每个元素上运行块。块内的代码在每个元素上运行to_s,将其从整数转换为字符串。然后,map方法返回一个包含转换项的新数组。

第一个例子是常见的并且被广泛使用。乍一看,第二个例子看起来有点神秘。让我们看看发生了什么:

在Ruby中,以冒号(:)为前缀的项目是符号。如果您不熟悉Symbol类/数据类型,我建议您使用Google,并在继续之前阅读几篇文章。 Ruby中的所有方法名称都在内部存储为符号。通过使用冒号为方法名称加前缀,我们不会将方法转换为符号,我们也不会调用方法,我们只是传递方法的名称(引用方法)。在上面的例子中,我们将to_s(它是对to_s方法的引用)传递给&符号(&)运算符,它将创建一个proc(通过在引擎盖下调用to_proc)。 proc将一个值作为参数,在其上调用to_s并返回转换为字符串的值。

尽管:to_s符号始终相同,但在运行map循环时,它将引用与每个数组项对应的类的to_s方法。如果我们将一个数组如[21,4.453,:foobar,]传递给map方法,则Fixnum类的to_s方法将被应用(调用)到第一个项目上,Float类的to_s方法将应用于Symbol类的第二个项和to_s方法将应用于第三个项。这是有道理的,因为我们没有将实际的to_s方法传递给&符号运算符,只是它的名称。

下面是一个创建proc的示例,该proc接受一个参数,在其上调用一个方法并返回该方法的结果。

p = :upcase.to_proc
p.call("foo bar")
Output:
=> "FOO BAR"

让我们回顾一下arr.map&:to_s

中发生了什么
  1. 在每次迭代迭代时,数组的一个项目(整数)传递给&:to_s
  2. :to_s符号(它是对to_s方法的引用)被传递给& operator,创建一个将接受参数(数组项)的proc,在参数上调用to_s并返回转换为字符串的值;
  3. map方法返回一个新数组,其中包含字符串" 1"," 2"," 3"," 4"和" 5"。