在 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
參考文件