從基礎的block談起
block是用來暫存及傳遞程式碼的地方
它本身不是物件(但有方法可以讓它變物件,最後說明)
所以必須掛在別人身上(可能是別的方法、別的物件)
我們在rails裡面常寫以下這種代碼
@users.each do |u|
...
end
這種結構我們稱作為block
目的是用來傳一段程式碼給.each方法
代表@users的每一個物件都會執行一次block的內容
另外一種簡化的寫法(當你的程式碼很短的時候可以縮成一行)
@users.each{|u| ...}
所以可以看的出來 do…end 就是代表 {...}
然後裡面的|u| 就是在程式碼中利用yeild方法傳遞過來的值
參考為你自己學ruby on rails 一書的內容
來自己創一個.times方法
def my_times(n)
i = 0
while n > i
i += 1
yield i*2 #對應到block裡面的||
end
endmy_times(3) { |num| #i*2就會yield到這邊
puts "hello, #{num}xRuby"
}#輸出 2xRuby 4xRuby 6xRuby
再提供一個例子
多個yield值 與 傳值 互動
def test_method(pass_value)
puts "pass: #{pass_value}" #下方呼叫之後 pass: 2
yield("a statement:", pass_value*3) #傳2*3給下面的block
endtest_method(2){|a,b| puts a+"#{b*4}"} #a statement: 24 (2*3*4)
可以自由回傳任意值,再讓使用者自訂動作
each是我們最常用的,其他的還有哪些
舉例一些比較難的
Map 方法
Map會對 list 這個陣列裡的每一個元素做某件事之後再收集成一個新的陣列,對map也可以使用
直接上程式碼
array=["a","b","c"]
puts array.map{|a| a+"p"} #返回一個陣列["ap","bp","cp"]
然後對hash也有用,第一個返回的值是key,第二個返回的值是value
如果只有設一個值 他就會餵一個[key,value]陣列給你
hash={"a"=>1,"b"=>2,"c"=>3}
puts hash.map{|a| a} #返回一個陣列["a",1,"b",2,"c",3]
puts hash.map{|a| a.to_s}
#返回一個陣列["["a",1]","["b",2]","["c",3]"]
#代表|a| 是一個含有key與value的陣列
這邊常見的問題,有些人想要用hash.map來做一個新的hash
但這就違背了map原本的設定(map就是用來做新的陣列)
但還是有方法可以達成
pair={"a"=>1,"b"=>2,"c"=>3}
Hash[pair.map{|key,value| [key, value*2] } ]
#產生一個新的hash {"a"=>2,"b"=>4,"c"=>6}
至於block的應用是很靈活的
可以利用do end做block,再餵新的值來做陣列
array=["a","b","c"]
array_2 = array.map do
|v|
if v=="a" or v=="b" #如果值是a或b,就產生true,否則false
true
else
false
end
end#array_2產生新的陣列 [true,true,false]
這邊要留意的是block不是function,不適用return的方法
block就只是預設回傳最後一個值
Reduce方法
Reduce覺得又更難了一些,他是一個用來做疊代、累加的工具
參考上面的連結,來思考下面的程式碼
reduce(initial) { |memo, obj| block } → obj
最常用、最完整的用法就是上面這串
reduce方便的用法就是有一個暫存值 memo,在疊代過程中保留,最後返回
然後最前面initial就是這個暫存值的初始值(也常用來設定型態)
最基本的應用就是把值全部sum起來
(1..8).reduce(0) { |sum, num| sum += num }
#最後返回 36 ,也就是從1加到8["a","b","c"].reduce("") { |sum, num| sum+=num+"p" }
#最後返回字串 "apbpcp"
#這邊的initial不可省略,不然會出錯
當然,Reduce也會拿來處理array或hash
舉例如下
a = {"a"=>100, "b"=>200, "c"=>300}
#想要把每個value加總
a.reduce(0) { |sum, e| sum += e[1] } #600
#這邊返回的e 代表[key,value]陣列array = [{"a"=>100}, {"b"=>200}, {"c"=>300}]
#想要把每個hash合併起來
array.inject({}) { |sum, e| sum.merge(e) }
#先inintial 讓block知道sum是雜湊型態
#e就代表一個雜湊 {key=>value}
#返回一個完整的hash {"a"=>100, "b"=>200, "c"=>300}
然後還有更極端的作法 就是reduce搭配symbol
(就是寫完之後 非ruby的工程師可能都不太懂?)
Proc
在講ruby block的時候,常常會看到Proc
但這兩個是不同層級的東西
block就只是一串程式碼的容器,他不能獨立運作
block可以藉由掛在function 或是 Proc,讓他執行容器中的程式碼
再繼續深入談,Proc是可以讓程式碼轉換成物件的方法
(廢話好多,總結就是Proc是物件、block不是)
p = Proc.new { puts "Hello World" } #使block物件化
p.call #用call來呼叫 # prints 'Hello World'
p.class # returns 'Proc'
a = p # a now equals p, a Proc instance
a # returns a proc object '#<Proc:0x007f96b1a60eb0@(irb):46>'
上面這個案例展現了p成為proc物件
而且可以傳遞,也就是a = p
(這邊要記住function不能傳遞,這也是proc的優勢)
再來,我們在上面有用block示範times的簡單寫法
但其實ruby原生寫法並不是傳block而已
而是傳proc進去
我們會常看到&block這個寫法
譬如在rails使用的form_tag,就是會掛一個&block在裡面
這個&的意思,就是把掛在方法後面的block轉成proc
通常會再搭配 block_given?的方法,來判斷block存在與否
def f2(n, &p)
if block_given?
p[n] # call proc p
# 'p[n]' can be alternated with 'p.call(n)'
# 'yield n' also works
else
puts 'no block'
end
end
f2(2){|n| n.times{puts 'iam block'}} #印2次'iam block'
f2(345) #印出 'no block'(無論傳甚麼n進去都不影響)
提醒這個&block 都會放在參數的最後
以下參考大神
另一個會一起談的就是lambda
lambda跟proc一樣都是屬於proc物件
lam = lambda { puts “Hello World” }
只是在使用上有些不一樣
譬如lambda會要求傳入的值須符合要求
(lam = lambda { |x| puts x },你就只能傳一個值進去)
附上簡單說明
更多實用技巧 參考JC大
遍歷對象的方法參考
這篇太燒腦了,先這樣
ㄅㄅ