在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 的用法。