iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
0
自我挑戰組

滿滿的紅寶石不拿嗎?-- 去吧!我把世界上的一切都放在那裡了! 系列 第 18

scope <> 你已經進到「死亡外科醫生」的領域了!ROOM! - 滿滿的紅寶石不拿嗎?

[Day18] 帥氣的特拉法爾加·羅要來告訴大家什麼是 scope!

嗨大家好!今天繼續講在 Ruby 裡要如何執行 Block,

不過在那之前,得先認識兩個概念:scopeyield
嗯...以前上英文課不是很認真,這兩個單字怎麼看怎麼陌生,就配著英英字典的解釋一起來看吧:

scope

  • an area in which something acts or operates or has power or control.

翻成中文大概就是「作用範圍、作用域」的意思,這個「作用」在程式環境裡指的是能不能生效,很多時候,程式碼只能在一小塊區域內具有效力 (這段文字仍然很抽象)

最常被提到的就是變數,舉個例子,如果像這樣:

i = 9527
def serial_number
 puts i 
end

serial_number

那麼會印出:

NameError (undefined local variable or method `i' for main:Object)

儘管在第一行已經定義變數 i 的值為 9527,但方法 serial_number 還是找不到要印出來的 i 是什麼,對於程式而言,方法(method)的裡面和外面會被劃分為不同的 scope,類別(class) 也是同樣的狀況。


yield

  • relinquish to the control of another.

翻成中文就是「放棄控制權給其他人」,在程式環境裡,我們會透過 yield 讓方法把控制權暫時轉交給其他程式,什麼意思呢?讓我們繼續看下去~

如何正確使用 yield

雖然昨天好像就有提過,但我這個金魚腦也忘了到底講了什麼,所以還是舉個例子吧:


def we_are_strong
  puts "雄壯!"
  yield
  puts "嚴肅!"
end
 
we_are_strong {
  puts "威武!" 
}

# 印出
雄壯!
威武!
嚴肅!

在上面這段程式裡,我在呼叫 we_are_strong 方法時再接一個 Block ,當程式開始執行會依照這樣的路徑:

  1. 先執行 puts "雄壯!"

  2. 接著執行 puts "威武!",因為 we_are_strong 裡寫了一個 yield ,它會讓程式執行到那一行時,把控制權轉出去(離開這個方法)

  3. Block 收到轉出來的控制權,執行自己的動作

  4. Block 執行完後,又會把控制權還給方法

  5. 執行 yield 之後的程式

這樣就能讓 Block 在程式裡動起來啦!


yield 還可以攜帶參數

像是這樣:

def method_yield
  yield 3
end

method_yield { |n|
  puts "Hi! " * n
}

# 印出
Hi! Hi! Hi! 

不過如果 yield 後面沒寫什麼,就等於是帶了一個 nil 參數。

def method_yield
  yield nil
end

method_yield do
  p "a"
  p "b"
end

# 印出
"a"
"b"

甚至也可以帶多個參數轉出去,而且數量不一樣的話就會發現:

def method_yield
  yield 1, 2
end

method_yield do |x, y, z|
  p x
  p y
  p z
end

# 印出
1
2
nil

讓出控制權來有什麼好處?

yield 可以幫助我們對資料進行過濾和篩選,譬如:

list = [* 1..10]
p list.select { |x| x > 5 }

# 印出
[6, 7, 8, 9, 10]

雖然我們看不到 Ruby 的原生方法 select 方法裡面寫什麼,但現在至少已經可以知道,在 select 方法裡一定有一行是 yield,會把控制權轉交後面接的 Block 計算。

Block 執行完也可以帶東西回來

俗話說「禮多人不怪」,既然方法裡的 yield 可以帶參數給 Block,那麼 Block 當然也可以帶東西回去給方法,一個回禮的概念 :D (我說你們感情真好啊!)

譬如:

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

test_two 是一個會檢查帶入參數是不是 2 的簡單方法。

如果帶入 3:

test_two(3){|n|
  puts "wait...let me check..."
  n == 2
}

# 印出
wait...let me check...
no, it is not 2

如果帶入 2:

test_two(2){|n|
  puts "wait...let me check..."
  n == 2
}

# 印出
wait...let me check...
yes, it is 2

等等!我發現它其實是叫 Block 去做事!(而 Block 也很乖地去做了)

Block 在執行完後會回傳 truefalse,然後 test_two 就只是依照這個回傳值來印出相對應的東西,方法內部根本就找不到檢查的機制啊!


沒有 block 但是卻用了 yield 就會出錯

LocalJumpError

def hello
  yield  
end
hello

# 印出
LocalJumpError (no block given (yield))

LocalJumpError 的意思是程式不知道你要 yield 到哪裡去

在程式裡,yield 和 block 似乎有一種相對應的共生關係,只是如果在方法裡寫了 yield ,外面卻沒有接收控制權的 Block 的話,Ruby 會噴錯告訴你 LocalJumpError ,意思是程式不知道你要 yield 到哪裡去...

反之,如果只寫了 Block 但方法裡沒有 yield 的話,Block 也只會傻傻地站在方法外面等到天荒地老,直到有一天眾人將它遺忘啊...

所以,請記得讓 yield 和 block 同時出現吧!


介紹就到這邊,希望大家能了解 Ruby 裡的 Block 是什麼了!(也希望之後自己回來看這段不要太精彩)


上一篇
Block <> 只是想被人需要,我很樂意為你執行! - 滿滿的紅寶石不拿嗎?
下一篇
Proc & Lambda <> 通通都拿出來變成實用的武器吧! - 滿滿的紅寶石不拿嗎?
系列文
滿滿的紅寶石不拿嗎?-- 去吧!我把世界上的一切都放在那裡了! 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言