iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
自我挑戰組

入坑 RoR 必讀 - Ruby 物件導向設計實踐系列 第 25

Day25 Ruby物件導向實踐-block, proc, lambda

  • 分享至 

  • xImage
  •  

今天的主題很老掉牙,你打關鍵字google會有一堆文章出現,至於為什麼還要寫呢,因為我開心你管我,因為在實作時超常碰到,必須要搞清楚才行,也趁此機會再摸得更熟。

Block

定義

在ruby中唯一不是物件的東西,它允許你將一段程式碼包裝在一個可被傳遞和執行的區塊中,花括號 {} 與 do..end 就是 Block。要接在方法(method)後面,且無法單獨存活,會出錯,最後一行的執行結果就是回傳值。

[1, 2, 3].each { |x| puts x }

# or

[1, 2, 3].each do |x|
  puts x
end

特性

ruby貴為一個物件化很徹底的語言,怎麼會容許不是物件的存在呢?他有什麼長處呢?

  1. 匿名:不需要命名,並且可以直接在方法調用中定義。
  2. 可傳遞:可以作為方法的參數傳遞,也可以被傳遞給其他方法。
  3. 執行時延遲:只有在需要時才執行,這使得它們非常有用,例如在迭代集合或處理回呼時

傳遞Block給方法

通常使用yield 來執行Block,程式碼執行到puts my_method時,會執行my_method方法,先puts "Start of method",然後撞到yield,將控制權交給Block去執行puts "哈囉!我是 Block",執行完畢再將控制權交還my_method方法,然後puts "End of method"

def my_method
  puts "Start of method"
  yield
  puts "End of method"
end

my_method do
  puts "Inside the block"
end

puts my_method { puts "哈囉!我是 Block"}

#依序印出
Start of method
哈囉!我是 Block
End of method

block_given?

用於檢查當前方法是否接收了一個 Block。這個方法通常在方法內部使用,以便根據是否有 Block 來執行不同的程式碼,當方法中有yield,要判斷if block_given?,不然在外面呼叫方法但後方沒有 Block 時會噴錯。

def my_method
  yield if block_given?
end

my_method # 正常運作 但不會印出任何東西
my_method { puts "哈囉!我是 Block"} # 印出 哈囉!我是 Block
my_method do puts "哈囉!我是 Block" end # 印出 哈囉!我是 Block

花括號{}或是 do…end

  • do...end是 Ruby 中的多行 Block 語法,通常用於多行程式碼塊。它使你可以在多行中定義 Block 內的程式碼。
  • {} 花括號是 Ruby 中的單行 Block 語法,通常用於簡單的程式碼塊,可以簡潔地將 Block 定義在一行上。
  • 花括號{}優先權高於 do…end
list = [1,2,3,4,5]  
p list.map { |x| x + 2 }  # [3, 4, 5, 6, 7]

p list.map do |x| x + 2 end #<Enumerator: [1, 2, 3, 4, 5]:map> 只執行到 p(list.map) 就結束了

Proc

定義

可被保存在變數中的程式碼塊,它可以被多次調用。Proc 通常用於封裝一個或多個程式碼塊,使其可以像一個變數一樣傳遞和使用,可以使Block物件化,並單獨存在。

特性

  1. 匿名:一樣不需要命名,它們可以直接在程式碼中定義。
  2. 可傳遞:可以作為參數傳遞給方法,也可以被傳遞給其他方法。
  3. 可被保存:Proc 可以被保存在變數中,以便稍後使用。
  4. 閉包性:它們可以捕獲和保留它們創建時所在的作用域中的局部變數。
  5. 多次調用:Proc 可以被多次調用,每次調用都執行存儲的程式碼塊。

建立Proc

使用 Proc.newproc 方法來產生Proc物件。

# 方法1
proc1 = Proc.new { |x| x * 2 }
proc2 = Proc.new do
	|x| x * 2
end

# 方法2
proc3 = proc { |x| x * 2 }

調用Proc

  • 初階:
    以下五種方法都可以調用Proc物件。

    hello_world = Proc.new { puts 'hello!' }
    
    hello_world.call
    hello_world.() 
    hello_world []      # hello! => nil
    hello_world ===
    hello_world.yield
    
  • 進階:

    • Proc傳遞參數,不會檢查參數數量。

      my_proc = proc { |x, y| x + y }
      my_proc.call(1, 2)     # 正確,返回 3
      my_proc.call(1)        # 正確,返回 1
      
    • Proc作為參數傳遞

      hello_world = Proc.new { |name| puts "hello #{name}"}
      
      def perform_operation(x, operation)
        result = operation.call(x)
        puts result
      end
      
      perform_operation("Lilith", hello_world)  # hello Lilith
      

Lambda

定義

與Proc相似,通常用於封裝一個或多個程式碼塊,使其可以像一個變數一樣傳遞和使用,可以使Block物件化,並單獨存在。

特性

  1. 匿名:一樣不需要命名,它們可以直接在程式碼中定義。
  2. 可傳遞:可以將Lambda作為參數傳遞給方法,也可以將它們傳遞給其他方法。
  3. 閉包性:與普通的Proc一樣,可以捕獲和保留它們創建時所在的作用域中的局部變數。
  4. 嚴格參數檢查:對於傳遞的參數數量和參數的嚴格性檢查要更為嚴格。
  5. **可以使用 return :**當 Lambda 內執行到 return 後,會將控制權交回給呼叫它的方法,繼續執行接下來的程式碼。

建立Lambda

# 方法1
lambda1 = lambda { |x| x * 2 }

# 方法2
lambda2 = ->(x){ x * 2 }

調用Lambda

  • 初階:

    以下五種方法都可以調用Lambda物件。

    hello_world = lambda { puts 'hello!' }
    
    hello_world.call
    hello_world.() 
    hello_world []      # hello! => nil
    hello_world ===
    hello_world.yield
    
  • 進階:

    • Lambda傳遞參數,會對參數數量進行檢查,並回傳錯誤訊息。

      my_lambda = lambda { |x, y| x + y }
      my_lambda.call(1, 2)   # 正確,返回 3
      my_lambda.call(1)      # 錯誤,引發 ArgumentError
      
    • Lambda作為參數傳遞

      hello_world = lambda { |name| puts "hello #{name}"}
      
      def perform_operation(x, operation)
        result = operation.call(x)
        puts result
      end
      
      perform_operation("Lilith", hello_world)  # hello Lilith
      

參考資料:


上一篇
Day24 Ruby物件導向實踐-initialize 和attribute accessors
下一篇
Day26 Ruby物件導向實踐- 運算符
系列文
入坑 RoR 必讀 - Ruby 物件導向設計實踐30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言