本文截取一段 ruby doc 官網(wǎng) Proc 的內(nèi)容并翻譯堰乔,試圖更好的理解和總結(jié) Proc(non-lambda) 和 lambda 的區(qū)別和使用場景墓捻。
Lambda and non-lambda semantics
Procs are coming in two flavors: lambda and non-lambda (regular procs). Differences are:
In lambdas,
return
means exit from this lambda;In regular procs,
return
means exit from embracing method (and will throwLocalJumpError
if invoked outside the method);In lambdas, arguments are treated in the same way as in methods: strict, with
ArgumentError
for mismatching argument number, and no additional argument processing;Regular procs accept arguments more generously: missing arguments are filled with
nil
, single Array arguments are deconstructed if the proc has multiple arguments, and there is no error raised on extra arguments.p = proc {|x, y| "x=#{x}, y=#{y}" } p.call(1, 2) #=> "x=1, y=2" p.call([1, 2]) #=> "x=1, y=2", array deconstructed p.call(1, 2, 8) #=> "x=1, y=2", extra argument discarded p.call(1) #=> "x=1, y=", nil substituted instead of error l = lambda {|x, y| "x=#{x}, y=#{y}" } l.call(1, 2) #=> "x=1, y=2" l.call([1, 2]) # ArgumentError: wrong number of arguments (given 1, expected 2) l.call(1, 2, 8) # ArgumentError: wrong number of arguments (given 3, expected 2) l.call(1) # ArgumentError: wrong number of arguments (given 1, expected 2) def test_return -> { return 3 }.call # just returns from lambda into method body proc { return 4 }.call # returns from method return 5 end test_return # => 4, return from proc
直接翻譯:
Procs 有兩種形式:lambda
和 non-lambda (regular procs)
。它們的不同之處在于:
- 在
lambda
中朵诫,return
表示從該lambda
退出盯蝴; - 在
常規(guī) proc
中,return
表示從該方法退出(如果在方法外部調(diào)用抱既,將拋LocalJumpError
)职烧; - 在
lambda
中,對參數(shù)的處理方式與方法相同:strict
(嚴(yán)格),使用ArgumentError
表示參數(shù)編號不匹配蚀之,并且不對附加參數(shù)進(jìn)行處理蝗敢; -
常規(guī) proc
更慷慨地接受參數(shù):缺少的參數(shù)填充為nil
,如果proc
具有多個(gè)參數(shù)足删,則解構(gòu)單個(gè)Array
參數(shù)寿谴,并且在附加參數(shù)上不引發(fā)錯(cuò)誤。
個(gè)人更白話的理解:
常規(guī)(非lambda)proc | lambda |
---|---|
return 退出整個(gè) method
|
return 僅從 lambda 內(nèi)部退出 |
慷慨地接受參數(shù):缺少的參數(shù)填充為 nil 失受,如果 proc 具有多個(gè)參數(shù)讶泰,則解構(gòu)單個(gè) Array 參數(shù),并且在附加參數(shù)上不引發(fā)錯(cuò)誤 |
strict(嚴(yán)格)拂到,使用 ArgumentError 表示參數(shù)編號不匹配痪署,并且不對附加參數(shù)進(jìn)行處理 |
Lambdas are useful as self-sufficient functions, in particular useful as arguments to higher->order functions, behaving exactly like Ruby methods.
直接翻譯
Lambda
作為自給自足的函數(shù)很有用,特別是作為高階函數(shù)的參數(shù)時(shí)兄旬,與Ruby方法完全一樣狼犯。ps: 這句話可以幫助牢記,lambda
的檢查參數(shù)和 return
lambda
自身函數(shù)的性質(zhì)领铐。
Procs are useful for implementing iterators:
def test [[1, 2], [3, 4], [5, 6]].map {|a, b| return a if a + b > 10 } # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ end
Inside map, the block of code is treated as a regular (non-lambda) proc, which means that the internal arrays will be deconstructed to pairs of arguments, and return will exit from the method test. That would not be possible with a stricter lambda.
代碼執(zhí)行結(jié)果:
2.4.4 :005 > def test
2.4.4 :006?> [[1, 2], [3, 4], [5, 6]].map {|a, b| return a if a + b > 10 }
2.4.4 :007?> # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2.4.4 :008?> end
=> :test
2.4.4 :009 > test
=> 5
直接翻譯
Procs
在迭代中很有用悯森,在 map
內(nèi)部,代碼塊被視為常規(guī)(非lambda)proc
绪撵,這意味著內(nèi)部數(shù)組將被解構(gòu)為成對的參數(shù)瓢姻,而 return
將退出方法 test
。如果使用更嚴(yán)格的 lambda
莲兢,將不可能做到汹来。
個(gè)人白話理解
Procs
這種常規(guī)(非lambda)proc
方式,在 map
這種迭代器中是很好的改艇,上圖看執(zhí)行結(jié)果收班,可以實(shí)現(xiàn)在 map
內(nèi)部直接 return
整個(gè) test
方法。這比使用 lambda
更符合我們的代碼意圖谒兄。
The only exception is dynamic method definition: even if defined by passing a non-lambda proc, methods still have normal semantics of argument checking.
class C define_method(:e, &proc {}) end C.new.e(1,2) #=> ArgumentError C.new.method(:e).to_proc.lambda? #=> true
This exception ensures that methods never have unusual argument passing conventions, and makes it easy to have wrappers defining methods that behave as usual.
class C def self.def2(name, &body) define_method(name, &body) end def2(:f) {} end C.new.f(1,2) #=> ArgumentError
The wrapper def2 receives body as a non-lambda proc, yet defines a method which has normal semantics.
直接翻譯
唯一的例外是動態(tài)方法定義:即使通過傳遞 非lambda proc
進(jìn)行定義摔桦,方法仍然具有參數(shù)檢查的常規(guī)語義。此異吵衅#可確保方法永遠(yuǎn)不會具有異常的參數(shù)傳遞約定邻耕,并使包裝器輕松定義照常運(yùn)行的方法。包裝器 def2
將主體作為 非lambda proc
接收燕鸽,但定義了一種具有正常語義的方法兄世。
個(gè)人白話理解
例外的是,即使通過非 lambda 的 proc 去動態(tài)定義 method啊研,也會按照 lambda 的方式嚴(yán)格檢查參數(shù)御滩,這保證了動態(tài)定義方法的正常運(yùn)行鸥拧,不會出現(xiàn)和正常的 method
不同的行為。
另外我也從 Rails 的 scope 為什么用 lambda削解?Proc 與 lambda 有什么不同之處富弦? 這篇博文得到了些啟發(fā),我在記住這兩種 Procs
不同點(diǎn)的時(shí)候氛驮,時(shí)常忘記到底是 常規(guī) proc
檢查參數(shù)還是 lambda
腕柜,但如果能從「Rails 的 scope 為什么用 lambda?」這個(gè)問題來思考的話矫废,就更加的能記憶清楚了盏缤。Rails
的 scope
之所以用 lambda
就是因?yàn)?lambda
會嚴(yán)格檢查參數(shù)可以避免 Proc
對于「缺少的參數(shù)填充為 nil
」的行為導(dǎo)致 sql
查詢有誤。
總結(jié)
由上面分析可以看出磷脯,Proc(non-lambda)
和 lambda
都有各自的用武之處蛾找,通過對代碼的預(yù)期不同,來靈活使用從而達(dá)到準(zhǔn)確實(shí)作功能的意圖赵誓。從這點(diǎn)上來講,深入了解兩種 Procs
確實(shí)有助于我們寫出更合適的代碼并理解代碼本身柿赊。