Ruby:为什么这种使用map的方式会引发错误?

时间:2017-01-25 13:51:25

标签: ruby

我试图缩短

values.map { |value| value.gsub!("\n", ' ') }

values.map(&:gsub!("\n", ' '))

但它给了我一个:

SyntaxError:
...csv_creator.rb:40: syntax error, unexpected '(', expecting ')'
     values.map(&:gsub!("\n", ' '))

任何人都知道发生了什么?

3 个答案:

答案 0 :(得分:3)

&:method表示法使用#to_proc方法,该方法能够将符号转换为Proc个对象。如果需要为被调用的方法提供其他参数,则不能将其用作快捷方式。

有关#to_proc的更长解释可以在单独的答案中找到:What does to_proc method mean?

答案 1 :(得分:3)

&:foo可能被错误地视为&:foo(类似" pretzel colon"加强了这种错误观点。但是这里没有调用方法foo&:foo实际上是&:foo,后者是一个简单的符号。

调用方法时,&object(没有:)调用object.to_proc(应该返回Proc)并将返回的proc作为传递阻止参数到方法。

object经常恰好是一个符号,而Symbol#to_proc的实现在Ruby中看起来就像这样:(它实际用C语言编写)

class Symbol
  def to_proc
    proc { |object, *args| object.public_send(self, *args) }
  end
end

所以这个:

method(&:symbol)

实际上变成了这个:

method { |object, *args| object.public_send(:symbol, *args) }

或者,如果method没有产生多个值(例如map),那么它只是:

method { |object| object.public_send(:symbol) }

显然,你不能通过符号传递额外的参数。

但是...... object并不一定是一个符号。您可以使用另一个具有自定义to_proc实现的类。让我们滥用Array用于演示目的:

class Array
  def to_proc
    method, *args = self
    proc { |obj| obj.public_send(method, *args) }
  end
end

这个黑客会允许你写:

["foo\nbar", "baz\nqux"].map(&[:gsub, "\n", '-'])
#=> ["foo-bar", "baz-qux"]

答案 2 :(得分:1)

替代

字符串#方法

只是为了展示可以做的事情:你可以定义不带参数的String方法。

class String
  def replace_newlines!(replace = ' ')
    gsub!("\n", replace)
  end

  def replace_newlines(replace = ' ')
    gsub("\n", replace)
  end
end

p new_values = values.map(&:replace_newlines)
#=> ["1 2", "a b"]

p values.each(&:replace_newlines!)
#=> ["1 2", "a b"]

可悲的是,refinements wouldn't workto_proc

PROC

另一种可能性是定义新的Proc,而无需修补猴子String

my_gsub = proc { |string| string.gsub("\n", ' ') }
p new_values = values.map(&my_gsub)
#=> ["1 2", "a b"]

的每个/图/ GSUB / GSUB!

请注意map!方法一起使用时没有多大意义。你应该:

  • mapgsub
  • 一起使用
  • each使用gsub!