为什么:
[1,2,3,4,5].map(&:to_s) #=> ["1", "2", "3", "4", "5"]
工作但是:
[1,2,3,4,5].map(&:*(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
中发生了什么