Proc 與 lambda

本文截取一段 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 throw LocalJumpError 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 有兩種形式:lambdanon-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è)問題來思考的話矫废,就更加的能記憶清楚了盏缤。Railsscope 之所以用 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í)有助于我們寫出更合適的代碼并理解代碼本身柿赊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俩功,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碰声,更是在濱河造成了極大的恐慌诡蜓,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胰挑,死亡現(xiàn)場離奇詭異蔓罚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瞻颂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門豺谈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贡这,你說我怎么就攤上這事茬末。” “怎么了盖矫?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵丽惭,是天一觀的道長。 經(jīng)常有香客問我辈双,道長责掏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任湃望,我火速辦了婚禮换衬,結(jié)果婚禮上痰驱,老公的妹妹穿的比我還像新娘。我一直安慰自己冗疮,他們只是感情好萄唇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著术幔,像睡著了一般另萤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诅挑,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天四敞,我揣著相機(jī)與錄音,去河邊找鬼拔妥。 笑死忿危,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的没龙。 我是一名探鬼主播铺厨,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼硬纤!你這毒婦竟也來了解滓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤筝家,失蹤者是張志新(化名)和其女友劉穎洼裤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溪王,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腮鞍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莹菱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片移国。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖芒珠,靈堂內(nèi)的尸體忽然破棺而出桥狡,到底是詐尸還是另有隱情,我是刑警寧澤皱卓,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布裹芝,位于F島的核電站,受9級特大地震影響娜汁,放射性物質(zhì)發(fā)生泄漏嫂易。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一掐禁、第九天 我趴在偏房一處隱蔽的房頂上張望怜械。 院中可真熱鬧颅和,春花似錦、人聲如沸缕允。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽障本。三九已至教届,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驾霜,已是汗流浹背席舍。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工荸恕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓶蚂,地道東北人鸯两。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像蓉冈,于是被迫代替她去往敵國和親城舞。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容