iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
Modern Web

Ruby新手村的礦工日記系列 第 14

[ Day 14 ] Ruby 區塊一塊一塊~

  • 分享至 

  • xImage
  •  

在前面章節中,不曉得你有沒有看到像是這樣:

arr = [1, 2, 3, 4, 5, 6, 7]
p arr.reduce { |sum, x| sum + x }

1.upto(5) do |i|
  puts "hi, ruby #{i}"
end

{...} , do end這個就是一個區塊(block)。
Block 在 Ruby 或 Rails 裡會大量的看到他,所以這是非常常見的語法。
那 Block 是什麼呢?
他就是一段程式碼,一段不會被主動執行的程式碼,接下來就那你們進入 Block 的世界吧!

Block 有兩種?!

其實 Block 分成 {...}do ... end ,而且寫法是可以互換的:

# do ... end
5.times do 
  puts "Hello World!"
end

# 大括號
5.times {
  puts "Hello World"
}

但通常遇到就複雜的判斷會使用 do ... end ,但一行就能寫完的東西就會用{ ... }

Block 不是物件喔~

雖然 Ruby 是物件化的程式語言,但還是有例外,例如 Block 本身就不是物件。
記住 Block 是沒辦法單獨的存在,所以你執行下面兩行程式,就會出現語法錯誤。

{ puts "Hello, World!" }           # 語法錯誤
action = { puts "Hello, World!" }  # 語法錯誤

既然這樣會出錯,那要怎樣才能執行呢?
首先你必須給他欉控制權他才會執行,如下:

def go_where
  puts "go to aquarium!"
end

go_where {
  puts "today"
}

puts "go!go!go!"

這樣就可以成功印出:
go to aquarium!
go!go!go!

這時候,你就問為甚麼 today 沒有印出來!!
就讓我娓娓道來!

yield

yield
在馬路上,大家應該很常看到這個標誌吧!
看到他的時候,就要禮讓對方。
那在程式中寫下 yield 之後,他會把控制權暫時轉讓給 Block 。
讓我們來看看他怎麼把控制權轉讓出去吧!

def go
  puts "go to aquarium!"
  yield  
  puts "go to zoo"
end

go {
  puts "today"
}

puts "tomorrow"

這樣就會印出:
go to aquarium!
today
go to zoo!
tomorrow

Block 想要一些伴手禮~

在轉讓的同時,yield 會順便帶上一些伴手禮給 Block:

def one_num
  yield 5
end

one_num do |n|
  puts n    # 印出 5
end

def two_nums
  yield 12, 25
end

two_nums do |x, y|
 puts "#{x}月"  # 印出 12月
 puts "#{y}日"  # 印出 25日
end

Block 回傳值

其實 Block 也會帶一些東西回來,那他要怎麼帶東西回來呢?

def test_two
  if yield(5)
    puts "yes, it is 2"
  else
    puts "no, it is not 2"
  end
end

test_two {|n| n == 2 }

因為在 Block 的最後一行就會是他的回傳值,所以上面的程式碼會印出:no, it is not 2
另外,Block 也不會使用 Return :

p (1..10)select{ |m| m.even? }
p (1..10)map{ |n| n**2 }

如果你不小心用 return 回傳 Block 的結果:

p (1..10)select{ |m| return m.even? }

那就會出現 LocalJumpError 錯誤
因為 Block 不是一個方法,它不知道你要 Return 到哪而造成錯誤。

{...} 跟 do...end 的差別

最後我來說說這兩個的差別吧!
一開始有說這兩個是可以互換的,但是他們還是有一些小小的差別。
他們的差別在於優先權,如下:

p (1..5).map{ |n| n**2 }      # 印出 [1, 4, 9, 16, 25]
p (1..5).map do |n| n**2 end  # 得到 <Enumerator: 1..10:map>

會造成不同結果的原因,有點像是數學的「先乘除後加減」,{}的優先順序較高,其次是do end

讓我們來還原一下:

p ((1..5).map{ |n| n**2 })  
p ((1..5).map) do |n| n**2 end  # 優先順序較低,所以變成先跟 p 結合,造成後面的 Block 就被忽略了。

以上就是有關block的介紹。

參考資料:

  1. 為你自己學 Ruby on Rails

上一篇
[ Day 13 ] Ruby 今天我想來點迴圈跟迭代
下一篇
[ Day 15 ] Ruby 中的物件導向程式設計
系列文
Ruby新手村的礦工日記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言