在Block系列文章裡面
Proc 一共分為proc
、lambda
兩種物件。兩者功能大致上相同,不過因些微的不同而導致了極大的差別。個人推薦使用lambda
,因為其行為與一般的方法差別不大,不過以下會帶過Proc
, Lambda
之間的用法,畢竟雖然平常用不到,但面試卻很常被問到。
首先我們列出Proc
的特性
Proc.new {}
, proc {}
定義proc = Proc.new { |x| x }
#=> #<Proc:0x00007fd4de0c8248@(irb):8>
proc.call(1)
#=> 1
proc.call
#=> nil
proc
裡頭寫 return
就不會往下走了# 沒有return
def apple_machine(adjective)
proc = Proc.new { |x| x }
[proc[adjective], 'apple'].compact.join('-')
end
apple_machine('beautiful') #=> 'beautiful-apple'
# 有return
def apple_machine(adjective)
proc = Proc.new { |x| return x }
[proc[adjective], 'apple'].compact.join('-')
end
apple_machine('beautiful') #=> 'beautiful'
-> {}
, lambda {}
定義proc = -> (x){ x }
#=> #<Proc:0x00007fd4de10a580@(irb):18 (lambda)>
proc.call(1)
#=> 1
proc.call
#=> ArgumentError (wrong number of arguments (given 0, expected 1))
lambda
裡頭寫 return
就會繼續往下走# 有return
def apple_machine(adjective)
proc = -> (x){ return x }
[proc[adjective], 'apple'].compact.join('-')
end
apple_machine('beautiful') #=> 'beautiful-apple'
Day8 提過了基本的用法,這邊直接從實例開始介紹。未來學會proc
方法之後,若包成方法會導致增加程式碼的易讀性跟不必要性,或者只是懶得包方法的話,就可以使用呼叫Proc
的方式,宣告一個匿名函式,在同一個方法裡面使用。
若收到類別為陣列,將所有的元素前方加入井字號,並將陣列裡的元素全部用-
連起,並且在每一個元素前面+個#
字號印出來。
displayed_record = -> (opcode) { "##{opcode}" }
at = -> (opcodes) do
return displayed_record.call(opcodes) unless opcodes.is_a?(Array)
opcodes.map { |opcode| displayed_record.call(opcode) }.join('-')
end
# 使用方式
at.call(%w[50 51 54]) #=> "#50-#51-#54"
at.call('8000') #=> "#8000"
Day8 已經提到Proc.call
的四個語法糖,個人喜好的是第二種的.()
的寫法。漢漢老師內心總是想著,這種用法可以讓沒看過的人滿頭問號,若更偏激的軟體工程師甚至可能把.
改掉,這樣一來可以跟看到有人改壞我的code
了。
不過實際上這件事情並沒有真正的發生過,以上都只是自己純粹的想像跟腦補而已。
it_day10 = -> { puts "IT鐵人賽第10天" }
it_day10.call
it_day10.()
it_day10[]
it_day10.===
&
與在Ruby
的程式語言中,和C++
的不太一樣。Ruby
的&
可以將 proc
(or lambda
) 轉為 block
使用
# 將 proc 轉為 block
my_proc = Proc.new { |x| x + 2 }
[1, 2, 3].map(&my_proc)
=> [3, 4, 5]
# 將 proc 轉為 block
[1, 2, 3].map(&->(x) { x + 2})
=> [3, 4, 5]
&
也可以將 block
轉為proc
使用
def it_day9(&it_block)
it_block.call
end
# 直接使用 proc
it_day9(&->{ 'foo' })
# 將 block 轉為 proc 使用
it_day9 { 'foo' }
⭐️ &
搭配 symbol
會自動將symbol
轉為 to_proc
的用法。這邊可能比較難理解,重點只要記住,&
搭配符號,代表這個符號有proc
的功能,因此可以to_proc
,而這些可以to_proc
的方法通常都是Ruby
/ Rails
提供的方法。
# ruby 的 to_i, to_f, to_h 就是轉型態的方法,同樣的 to_proc 就是轉為proc(or lambda)
:+.to_proc.call(1,1) # 1+1
:upcase.to_proc.call('han') # 'han'.upcase
# 上面的意思等同於下面的意思
[[1,1], [2,2], [3,3]].map(&:sum)
#=> [2, 4, 6]
[[1,1], [2,2], [3,3]].map {|arr| arr.sum}
#=> [2, 4, 6]
今天的另外一個主角yield
。
Day9 我們提到了所有跟yield
的用法,如果只要會用法的話,看 Day9 就夠了!今天會從Proc
的角度來介紹block
。block
專有名詞稱為Explicit Blocks
,使用了&
跟 block name
來做表示。
以下一共有兩種方式可以將block
帶入使用
def it_day10(&it_block)
it_block.call
end
# 一共有兩種方式
it_day10(&->{ 'foo' })
it_day10 { 'foo' }
我們可以用 yield
改寫成下列的格式。yield
表示法有個專有名詞稱為Implicit Block
。yield
為block.call
的簡寫,可以不引入參數,也可以引入參數。
# yield without argument
def it_day10
yield
end
it_day10(&->{ 'foo' })
it_day10 { 'foo' }
然後,不能寫return
# yield with argument
def it_day10_arg(x = 1)
y = yield x
y + 1
end
it_day10_arg(2, &->(x) { x + 1 }) #=> 4
it_day10_arg(2) { |x| x + 1 } #=> 4
it_day10_arg(2) { |x| return x + 1 } #=> LocalJumpError (unexpected return)
yield
還可以搭配 block_given?
使用,可以區別block
和沒有block
的使用方法
def it_day10
block_given? ? yield : 'bar'
end
it_day10(&->{ 'foo' }) #=> 'foo'
it_day10 #=> 'bar'
block
差不多到這邊就講解完了,後續會介紹class
的用法。