首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Ruby 方法、代碼塊與閉包完整指南

     編輯:Java 相關知識

前言

Ruby 的方法、代碼塊、Proc、Lambda 與閉包構成了其強大的函數式程式設計能力。本文系統解析方法定義與呼叫、代碼塊與 Proc/Lambda 轉換、參數傳遞機制、閉包特性與實戰應用模式。

方法

定義與呼叫


# 基本語法
def method_name(param1, param2 = default_value)
  # 方法體
  last_expression # 隱式回傳
end

# 呼叫
method_name(arg1, arg2)
method_name arg1, arg2  # 省略括號

方法類型

類型定義位置呼叫方式範例
實例方法類別內obj.methoddef greet; end
類別方法類別內 self.Class.methoddef self.config; end
單例方法特定物件上obj.methoddef obj.special; end

參數機制


# 預設參數
def greet(name = "World")
  "Hello, #{name}!"
end

# 可變參數 (陣列)
def sum(*numbers)
  numbers.reduce(0, :+)
end
sum(1,2,3) # 6

# 關鍵字參數
def connect(host:, port: 80, timeout: 5)
  # host 必填,port/timeout 可選
end
connect(host: "localhost") # OK
connect(port: 8080)        # ArgumentError: missing keyword: host

# 雙星號收集關鍵字參數
def flexible(**options)
  options # => Hash
end
flexible(a: 1, b: 2) # {:a=>1, :b=>2}

# 區塊參數 (&block)
def with_logging(&block)
  puts "Start"
  result = block.call
  puts "End"
  result
end
with_logging { "Hello" }

特殊方法名約定

  • method=:設值方法 (setter),obj.name = "x" 實際呼叫 name=
  • method?:斷言方法,回傳布林,如 empty?, nil?
  • method!:危險/修改自身方法,如 sort!, save!

代碼塊

代碼塊是附著在方法呼叫後的程式碼片段,非物件,無法直接存放於變數。

兩種語法


# 大括號:單行慣例
[1,2,3].each { |x| puts x * 2 }

# do...end:多行慣例
[1,2,3].each do |x|
  puts x * 2
end

yield 呼叫代碼塊


def twice
  yield
  yield
end

twice { puts "Hello" }
# Hello
# Hello

# 傳遞參數給代碼塊
def with_user(name)
  yield(name.upcase)
end

with_user("alice") { |name| puts "Hi, #{name}!" } # Hi, ALICE!

block_given? 檢查代碼塊存在


def flexible
  if block_given?
    yield
  else
    "No block"
  end
end

flexible           # "No block"
flexible { "Hi" }  # "Hi"

Proc 與 Lambda:代碼塊物件化

建立方式


# Proc.new / proc (同義)
p1 = Proc.new { |x| x * 2 }
p2 = proc { |x| x * 2 }

# lambda / -> (Ruby 1.9+ 刺操作符語法)
l1 = lambda { |x| x * 2 }
l2 = ->(x) { x * 2 }      # 參數在箭頭後
l3 = ->(x, y=1) { x + y } # 支援預設參數

Proc vs Lambda 關鍵差異

行為ProcLambda
參數嚴格度寬鬆:多參數忽略、少參數補 nil嚴格:參數數量必須匹配,否則 ArgumentError
return 語意從定義 Proc 的方法返回從Lambda 本身返回
break 語意跳出定義處的迴圈/方法跳出 Lambda 本身 (需在迴圈中)
自我檢查proc.lambda? == falselambda.lambda? == true

return 差異實例


def proc_return
  p = Proc.new { return "from proc" }
  p.call
  "never reached"
end

def lambda_return
  l = -> { return "from lambda" }
  l.call
  "reached"
end

proc_return   # "from proc"  (直接從 proc_return 方法返回)
lambda_return # "reached"    (lambda 返回後繼續執行)

代碼塊、Proc、Lambda 互轉


# 方法接收代碼塊並轉為 Proc (& 參數)
def to_proc(&block)
  block # 這是一個 Proc 物件
end

my_proc = to_proc { "hello" }
my_proc.call # "hello"

# 顯式傳遞 Proc 給方法 (& 前綴展開)
def call_proc(p)
  p.call
end

call_proc(my_proc)           # 直接傳 Proc 物件
call_proc(&my_proc)          # & 將 Proc 轉為代碼塊傳遞

# 任何回應 to_proc 的物件都能用 & 展開
class Greeter
  def to_proc
    ->(name) { "Hello, #{name}!" }
  end
end

["Alice", "Bob"].map(&Greeter.new) # ["Hello, Alice!", "Hello, Bob!"]
# Symbol#to_proc 經典用法
%w[alice bob].map(&:upcase) # ["ALICE", "BOB"]

閉包

閉包 = 函數 + 其捕獲的詞法環境。Ruby 的 Proc/Lambda 都是閉包。


def counter
  count = 0
  -> { count += 1 } # 捕獲區域變數 count
end

c1 = counter
c1.call # 1
c1.call # 2

c2 = counter
c2.call # 1 (獨立環境)

# 閉包三大特性:
# 1. 變數可在方法外存取 (count 逃脫作用域)
# 2. 環境變數安全隔離 (c1, c2 各自 count)
# 3. 環境狀態持久化 (count 值保留)

實戰模式

1. 策略模式 (傳遞演算法)


def process(data, strategy)
  strategy.call(data)
end

upcase = ->(s) { s.upcase }
reverse = ->(s) { s.reverse }

process("hello", upcase)   # "HELLO"
process("hello", reverse)  # "OLLEH"

2. 建構器模式 (DSL)


class HtmlBuilder
  def initialize(&block)
    @html = ""
    instance_eval(&block) if block_given?
  end
  
  def div(content = nil, &block)
    @html << "
" @html << (block_given? ? instance_eval(&block) : content) @html << "
" end def to_s @html end end HtmlBuilder.new do div do div "Hello" div "World" end end.to_s # "
Hello
World
"

3. 惰性求值與備忘錄


def memoize(&block)
  cache = {}
  ->(key) { cache[key] ||= block.call(key) }
end

fib = memoize do |n|
  return n if n <= 1
  fib.call(n-1) + fib.call(n-2)
end

fib.call(10) # 55 (快速)

總結

Ruby 的方法是命名的可複用邏輯單元;代碼塊是附著於方法呼叫的隱式片段;Proc與Lambda將代碼塊物件化,前者語意寬鬆類似代碼塊,後者嚴格類似方法;兩者皆為閉包,捕獲定義時的詞法環境。掌握 yield、&block、Proc/lambda、return/break 語意差異,能寫出極具表達力的 DSL、策略模式、建構器與惰性計算等進階模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved