上一節(jié)我們講到了Ruby塊block的一些大致概念和定義方式,也說到了,塊的使用在提升代碼抽象和復用方面有很大的幫助,這一節(jié)我們談?wù)剦K的“包裝”類型Proc類
Intro
為了引出Proc類,我們稍微展開一下,在使用block的時候,block在使用的時候其實就是把它當匿名函數(shù)在傳遞,隨寫隨用,大多數(shù)的使用情況下比如在我們自定義的方法結(jié)尾排序好的返回值的時候,我們秩序在最后一步可能調(diào)用個Array#sort_by方法,注入我們需要的排序的邏輯即可,并不需要事先定義一個不需要的通用發(fā)發(fā)或者啥的,然后指定,便利性非常高。
Proc讓塊使用更方便
但倘若需要復用代碼塊或者突破限制在其它的地方也能使用傳遞來的代碼塊的話,將代碼塊類型化,會更加的合適和方便:
def total2(from, to, &block)
result = 0
from.upto(to) do |num|
if block
result += block.call(num)
else
result += num
end
end
return result
end
這是上節(jié)例子使用Proc類的改寫方式,最后一個參數(shù)前的&符號會將傳遞的塊自動轉(zhuǎn)化為Proc類,然后Proc類調(diào)用的時候通過block#call的方式把塊當方法一樣調(diào)用得到使用結(jié)果,在這里使用block其實可以幫助用戶擺脫ruby中常規(guī)情況下使用block是夠多語法糖帶來的困擾和不解,羅杰更佳的清晰和透明化。
Proc讓塊使用更自由
另外,突破限制的好處之一就是非常方便的傳遞給其它方法,把塊的使用delegate下去:
def call_each(ary, &block)
ary.each(&block)
end
call_each [1, 2, 3] do |item|
p item
end
=> 1 2 3
Proc類的定義
Proc類的定義方式非常簡單,把原來塊的內(nèi)容傳遞給Proc類的new方法就行:
hello = Proc.new do |name|
puts "Hello, #{name}."
end
hello.call("World") #=> Hello, World
創(chuàng)建出來的Proc類相當于給了塊一個類型和復用的錨點,給了ruby代碼實現(xiàn)抽象邏輯更多的自由,這應(yīng)該也算是ruby多編程范式的一個體現(xiàn)。
block vs lambda
理論上來說,如果在一個方法中返回了一個Proc對象,這個方法的返回值其實可以當做函數(shù)進行調(diào)用:
def test_proc(n)
Proc.new do |x|
puts x ** n
end
end
cube = test_proc(3)
puts cube.call(2) => 8
puts cube.call(3) => 27
puts cube.call(4) => 64
這其實就相當于一種嵌套函數(shù),而且形成了一個閉包,將父函數(shù)的變量捕捉了,運行的結(jié)果也能證明。但有個詭異的問題是,如果返回的Proc類型實例里面含有return這種退出指令,會有意想不到的現(xiàn)象:
def test_proc(n)
Proc.new do |x|
return x ** n
end
end
cube = test_proc(3)
puts cube.call(2) => 8
=> in `block in test_proc': unexpected return (LocalJumpError)
Proc提前返回了,索性還有l(wèi)ambda,出去參數(shù)檢驗更嚴格之外,lambda在這種情況下也能正確處理:
def test_proc(n)
lambda do |x|
return x ** n
end
# exponential = -> (m) {return m ** n}
end
cube = test_proc(3)
p cube.call(2) => 8
to_proc
很多方法有to_proc方法,借助于&符號,代碼塊和Proc能夠方便的互相轉(zhuǎn)化:
%w(42 39 56).map{|i| i.to_i } => [42, 39, 56]
VS
%w(42 39 56).map(&:to_i) => [42, 39, 56]
總結(jié)
這些也只算事對block和Proc的一個大致的總結(jié)筆記,希望對你有用。
參考資料: Ruby基礎(chǔ)教程