Block and Proc
Ruby 可以將程式碼參數化,Ruby 稱被參數化的程式碼為 block 。Ruby 語法以 {||}
表示一個 block ,其中的 ||
為參數列宣告,若無參數則可省略。
Ruby 的 Proc
類似 ECMAScript 的 Function
。在 ECMAScript 中使用關鍵字 function
即可配置一個 Function
實例。 Ruby 則使用 Kernel::proc、Kernel::lambda 方法 (兩者相同) 或是直接建構一個 Proc
實例(Proc.new
),需提供一個 block 作為引數。
Ruby: proc { |arguments| codes } ECMAScript: function(arguments) { codes }
Block and Proc
Block 無法單獨存在,只能作為 Ruby 指令或呼叫方法時的引數。Ruby 會主動將 block 參數化,程序員僅需利用流程指令 yield
即可將流程轉移到被參數化的 block 中運行。
Block way. 這種用法較常見。
def nP(n)
if block_given?
yield n # yield to black
else
puts 'no block'
end
end
Proc
way. 細節後述。
def nP(n, &p)
if block_given?
p.call n # call proc p
else
puts 'no block'
end
end
用例:
nP('hello') {|n| puts n}
nP(10) do |n|
n.times do
puts 'a'
end
end
不論是用 block 或是 Proc ,都可以用 Kernel::block_given? 方法判斷使用方有無傳遞 block 。
Proc::call 方法也可以運算子 [ ]
形式調用。上例之 p.call n
亦可寫成 p[n]
。
def nP1(n)
if block_given?
yield n # yield to black
else
puts 'no block'
end
end
def nP2(n, &p)
if block_given?
p[n] # call proc p
else
puts 'no block'
end
end
ECMAScript 只有 Function
類,沒有 block 與 proc 的區分。上述的 Ruby 程式,以 ECMAScript 表達如下列所示:
function nP(n, p) {
return p(n);
}
nP(10, function(n){print(n)});
nP(10,
function(n) {
for (var i = 0; i < n; ++i)
print('a');
}
);
Block and Method
定義方法時,若參數名稱前冠上 &
符號, Ruby 將自動轉換 block 為 Proc 實例(隱性調用 Proc.new),令其成為具名參數。
def nP(n, &p)
p.call n
end
def mP(m, p)
p.call m
end
nP('john') {|name| puts name}
mP('bob', Proc.new {|name| puts name} )
mP('bob', proc {|name| puts name} ) # Kernel::proc
大多數情形,我們只需要傳遞一段程式 (一個block) ,所以 Ruby 提供了自動轉換 block 為 Proc 實例的機制。作為待轉換為具名參數的程式碼區塊引數,必須位於方法定義之參數列的最後一個。
如果要傳遞多段程式,則不適用上述轉換機制。程序員必須明確指示處理動作。
def xP(n, &p1, &p2) # ERROR!
end
def xP(n, p1, p2)
p1.call n
p2.call n
end
xP(5,
proc {|n| puts n },
proc {|n| n.times {puts 'a'} }
)
自動轉換 block 為 Proc 實例的機制通常應用於動態繫結的場合。
class DP
def initialize(&p)
@do = p
end
def do(n)
@do.call n
end
end
d = DP.new {|n| puts n}
d.do 5
Block 與 Closure 的應用可參考 Martin 《Closure》。
樂多舊回應