Leon's Blogging

Coding blogging for hackers.

Ruby - &: And &method With To_proc

| Comments

在 ruby 中很常看到以下的寫法,那為什麼可以簡寫成 (&:to_s)

1
2
[1, 2, 3].map { |num| num.to_s } #=>  => ["1", "2", "3"]
[1, 2, 3].map(&:to_s) #=>  => ["1", "2", "3"]

& 實際上是會觸發物件的 to_proc 方法,把物件轉換為 Proc,並嘗試指定給 & ,因此可以在物件上定義 to_proc,然後使用 & 來觸發

可以看到,建立一個自己的 to_proc 把原本的覆蓋掉,並加上一些訊息

1
2
3
4
5
6
7
8
9
10
class Symbol
  def to_proc
    puts "self is #{self}"
    Proc.new { |args| args.__send__(self) }
  end
end

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

symbol 都會有 to_proc method

1
2
3
4
5
6
7
8
9
10
:symbol.methods
# => [:to_proc, ..]
:upcase.to_proc
# => #<Proc:0x007fb2410f3e30(&:upcase)>
:upcase.to_proc.call('abc')
# self is upcase
# => "ABC"
p = Proc.new{ |n| n ** 2 }
[1, 2, 3].map(&p)
# => [1, 4, 9]

但是在 reduce/inject 卻可以將 & 拿掉

1
2
3
4
5
6
7
[1, 2, 3].reduce(&:+) # => 6
[1, 2, 3].reduce(:+)  # => 6
[1, 2, 3].reduce('+') # => 6

[1, 2, 3].inject(&:+) # => 6
[1, 2, 3].inject(:+)  # => 6
[1, 2, 3].inject('+') # => 6

&method

Looks up the named method as a receiver in obj, returning a Method object (or raising NameError). The Method object acts as a closure in obj’s object instance, so instance variables and the value of self remain available.

如果要將原本的值變成參數,就要改用 &method

1
2
[1, 2, 3].map { |num| puts(num) }
[1, 2, 3].map(&method(:puts))

to_proc 特殊用法

ruby 2.6.0

1
2
3
4
5
6
h = { a: 1, b: 2, c: 3, d: 4 }
[ :a, :c, :d, :b ].map(&h)
# => [1, 3, 4, 2]

# like
[ :a, :c, :d, :b ].map { |a| h[a] }

實現的 code 像這樣

1
2
3
4
5
6
class Hash
  def to_proc
      puts self
    lambda { |v| self[v] }
  end
end

參考文件

Comments