Ruby & RailsRuby

06.Ruby學習分享之閉包

2017-03-26  本文已影响39人  清水川崎

上次的作業

  對於arr = [1,2,3],寫一個singleton method,將自己變成[2,4,6]
  這道題的核心代碼就是遍歷。如果使用each遍歷,它不會對元素做改變。如果使用map,它會返回新的實例化對象,不合題意。在迭代器不行的情況下,我們可以使用以前提到的forin。讓我們看下參考代碼

arr = [1,2,3]
def arr.double!()
#我們使用forin對數組遍歷,不取數組長度的末尾,因為數組的下標是從0開始。每個遍歷出來的對象,讓它進行乘等運算。
    for i in 0...self.length
    self[i] *= 2
    end
end

  作業就到這裡吧,看看今天的內容——閉包


  閉包其實分為三個地方學習,第一個是{},第二個是Proc類的proc實例化對象,第三個也是隸屬於Proc類lambda.
  我們先看下{}
  blcok寫法有兩種形式,一種為do end,一種為{}。我們看下下面的例子
  像這樣的當你的block代碼有多行的時候,我們就使用do end

arr.each do |elem|
  foo(elem)
  bar(elem)
  baz(elem)
end

  而假如你的代碼只有一行的話,我們就使用{}

arr.each {|e| p e}

  當然有的時候,為了不發生誤會,我們必須使用{},因為它的優先級很高

scope :foo,lambda {|elem|
foo(elem)
bar(elem)
baz(elem)
}

  上面的代碼是指有一個方法叫做scope,需要傳入兩個參數,第一個參數為一個方法名,第二個參數為一個帶block的參數。
  這裡我們使用了{}就可以讓其和lambda綁定在一起。

block方法的兩種方式

  第一種傳入式,為了區分參數和block參數,我們需要在其前面加一個&,然後調用call方法,將它傳給方法調用時後面的block

def foo(&block)
    a = 2
    block.call(a)
end

  call就把a傳給了後面的{}

foo {|a|p a}

  第二種方式就是使用yield

def foo2
    a = 2
    yield a
end
foo2 {|a| p a}

  這裡我們把a,通過yield方法傳給了{},後面調用時,我們就可以在{}輸入代碼進行操作
  block是可以拿到外界定義的變量的
  比如

b = 3
foo2 {|a|p a + b}

  很明顯,a是我們yield出來的a,b是我們剛剛定義的3,所以terminal打印了5

接著說下block的return問題。

  看下面的例子。猜一下它的打印結果
  是打印(foo3.start,bar.end)還是(foo3.start)?

def bar
    x = 3
    yield x
    p 'bar.end'
end
def foo3
    p 'foo3.start'
    bar {|x| return if x > 0}
    p 'foo3.end'
end
foo3

  正確結果是foo3.start
  因為首先看下foo3這個方法,我們的x=3,所以它會被return,也就造成了不會走下面的p 'foo3.end'。但是為什麼沒走bar.end了?這是因為我們調用bar的時候,x滿足了>0的條件,所以跳出了所有的代碼塊
  所以也就不管p 'bar.end'的事情。

  如果在foo3中調用bar這樣寫bar {p '123'},那麼結果就會是("foo3.start","123","bar.end","foo3.end")
  因為p '123'被傳入了bar方法體內,先執行foo3方法內部的p 'foo3.start',接著調用bar,bar中的yield會接收 p '123',最後回到foo3中,調用p 'foo3.end'
  除了return,大家也可以試下next、break看看其他效果

Proc

  我們不得不說下Proc這個東西,它是一個類,而proc和lambda是它的兩個實例化
  剛剛我們說block方法的第一種,其實那個&block就是個proc,不然怎麼可能可以調用call方法。
  這個東西可以和block互換,比如下面的例子

arr = %w(a b c)
p arr.map(&:to_sym)
p arr.map {|m|m.to_sym}

  你會發現兩種結果是一樣的,to_sym方法被當做了block傳入了進去
  當然proc也支持直接定義

proc = Proc.new {|x,y|p x,y}
p proc.call 3

  定義以後,我們就可以直接使用call方法,傳入的3可以被proc使用

跟proc相似的還有個lambda

  lambda是這樣定義的

l = lambda {|x,y|p x,y}

  但是proc和lambda有很大的區別,一個是block,另外一個更像一個方法
  前面的proc使用call方法參數可以有任意多,可多可少,會自動nil或者忽略
  但是lambda就不行,你定義的參數有多少個,一個不能多也不能少

l.call 1,l.call 1,2,3#這就是錯誤的
l.call 1,2#這個就是對的

  因為更像方法,所以定義的時候,我們也可以給它來個缺省值

l = lambda {|x,y = 3|p x,y}
l.call 1

  我們再來多參數轉數組

l = lambda {|x,*y|p x,y}
l.call 1,2,3,4,5

  是不是這樣就可以可多可少的參數了?
  由於兩者不同,所以proc中要是直接出現return會報錯,因為必須包含在方法體內部,而lambda則不會。

因為它更像一個方法

今天的作業

  創建一個文件名為start_with.rb,給它添加一個名為each_starts_with的方法。
  功能如下:
  第一個參數為字符串數組,第二個為字符串,要求利用block打印從字符串數組中以第二個參數開頭的字符串。
  參考調用如下:

each_starts_with(%w(abcd axyz able xyzab qrst),'ab'){|s|p s}

  它會打印出:"abcd","able"

上一篇 下一篇

猜你喜欢

热点阅读