JT's Blog

Do the right things and do the things right.

Ruby - Proc / Lamdba / Yield

| Comments

Ruby Method &block 提到要傳入 Proc Class 執行邏輯運算

順便寫一篇記錄Proc Class 的基本應用,不是很好入門,但是多看幾次就能慢慢了解其中意義

Proc

Proc 為一種匿名區塊(Block) 物件,根據傳入的參數執行邏輯運算

如果傳入數量與宣告不同,則以nil 替代

宣告 & 執行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 宣告1
proc1 = Proc.new do |x|
    puts x
end

# 宣告2
proc2 = proc do |x|
    puts x
end


# Proc#call
proc1.call("hello world")  # hello world

# Proc#[]
proc2["hey, how are you?"] # hey, how are you?

Feature of Closure

可透過Proc 達到closure 的目的

1
2
3
4
5
6
7
8
9
10
11
def counter
 c=0
 Proc.new do c += 1 end
end

c1 = counter
c2 = counter

c1.call # 1
c1.call # 2
c2.call # 1

Lambda

Proc 的另一種寫法,稱為 lambda。創建出來的也是Proc 物件,通過 lambda 建立的Proc 其行為更接近method。

可以使用 -> 簡寫 lambda

-> == Dash Rocket

1
2
3
4
5
lamdba { |x| x*2 }

# equivalent to

->(x) { x*2 }

宣告 & 執行

1
2
3
4
5
6
7
8
9
10
11
lambda1 = lambda do |x|
  return x
end

lambda1.call("hello world")
lambda1["hey, ok"]

# 另一種宣告方式 ->(參數) { 邏輯處理 }
lambda2 = ->(x) { return x * 2 }
lambda2.call("hello world")
lambda2["ok, fine!"]

與Proc 差異

  1. 傳入參數數量需與宣告時一致
  2. (重要)可以使用return 將值傳回 (Proc 遇到return 就會跳出區域並結束)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def power_of(n)
  lambda do |x|
    return x ** n
  end
end

cube = power_of(3)
cube.call(5)  # 125

# using Proc
def power_of(n)
  Proc.new do |x|
    return x ** n
  end
end
cube = power_of(3)
cube.call(5)  # LocalJumpError

to_proc 方法

有些object 可以使用#to_proc,如果傳入的參數有加上 & Symbol,則#to_proc就會自動調用,進而生成Proc Object

可參考如何應用在 collection

1
2
3
4
5
%w(42 52 14).map { |n| n.to_i }

# 上下相同

%w(42 52 14).map(&:to_i)

yield

蠻頭痛的,這題不知道要寫在ruby method 還是在這,但思考之後,認為先有了Proc 的觀念,再來討論這個,或許會更了解吧!

這有點類似 Java 的abstract class,將method 要實作的部分放yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def my_method_no_arg
  puts "warm up 1"
  yield
end

def my_method_with_arg
  puts "warm up 2"
  # yield('james')
  yield(%W(a b))
end

my_method_no_arg do
  puts "running alone"
end

my_method_with_arg do |friends|
    # puts friends.class
    if friends.class == String
      puts "running with #{friends}"
    else
      friends.each do |friend|
        puts "running with #{friend}"
      end
    end
end

總結

  • yield = &block + block.call()
  • &block = Proc | Lambda

Reference

  • RailsFun.tw 新手教學
  • Ruby Programming ver.4

Comments