Preface
在 迭代器與程式區塊 中談過,每次呼叫方法時,其實會涉及四個部份:
程式區塊與 Proc
如果你在方法最後一個參數設定 & 開頭的參數,則會使用區塊建立 Proc 物件傳入,Proc 有個 call 方法,用以執行 Proc 物件內含的程序。例如:
你可以使用 Proc.new 建立程序物件。例如:
Proc.new 會使用指定的程式區塊建立物件,呼叫 call 時,就是執行程式區塊指定的程式流程。要注意的是,Proc 物件是 Proc 物件,程式區塊是程式區塊,兩者根本上不同,程式區塊是定義方法時語法的一部份,呼叫方法時指定程式區塊,如果方法最後有個 & 參數,Ruby直譯器會使用方法上指定的程式區塊來建立 Proc 物件。例如:
- foreach([1, 2, 3]) { |element| puts element }
- p = Proc.new { |element| puts element }
- foreach([1, 2, 3], &p)
所以任何可以接受程式區塊的方法,如果想要自行建立 Proc 物件傳入,都要加上個 &。例如你有個想重用的程序,則可以使用 Proc 而不是程式區塊:
不過如下指定就錯了,因為方法最後一個參數不知道該使用傳入的 Proc,還是捕捉程式區塊而建立的 Proc:
實際上,& 會觸發物件的 to_proc 方法,並嘗試指定給 & 變數,你可以在任何物件上定義 to_proc 方法,然後使用 & 來觸發 to_proc 方法。例如:
- class Ball
- attr_reader :radius
- def initialize(radius)
- @radius = radius
- end
- def self.to_proc
- Proc.new { |ball| ball.radius }
- end
- end
- # 收集球的半徑
- print [Ball.new(10), Ball.new(20), Ball.new(30)].collect(&Ball) # [10, 20, 30]
則可以改用以下:
有些方法可以直接傳入 Symbol 的,也是類似的道理。例如陣列的 reduce 方法,為了方便,甚至設計為可省略 &:
實際上,Symbol 的設計大致是:
- class Symbol
- def to_proc
- Proc.new { |o| o.send(self) }
- end
- end
Proc 的 call 方法可以接受任意引數,不過實際上你可以取得幾個引數,在於你定義了幾個區塊參數。例如:
Proc 正如其名,是一小段程序,一小段流程,要注意若在建立 Proc 時的程式區塊 return 時的狀況。例如:
注意到並沒有顯示 "some 2",因為上例相當於:
- def some
- puts "some 1"
- puts "執行 Proc"
- return
- puts "some 2"
- end
因為設計 API 時,並不希望有 return 中斷了原本 API 的執行流程,因此 Ruby 執行時如果看到 return,就會視為錯誤,即使 return 的目的是正常結束並傳回值,如果你確實是想傳回值,可以不撰寫 return,因為 Ruby 執行流程中最後一個物件就會被當作傳回值。例如:
因為 Proc 像是個執行流程而不是方法,除了要注意 return 之外,迭代器與程式區塊 中也提到,要注意程式區塊中撰寫了 break、next 或 redo 的結果。
沒有留言:
張貼留言