name: ruby-blocks-procs-lambdas user-invocable: false description: 用于处理 Ruby 块、proc、lambda 和功能编程模式,包括闭包和高阶函数。 allowed-tools:
- Bash
- Read
- Write
- Edit
Ruby 块、Proc 和 Lambda
通过块、proc 和 lambda 掌握 Ruby 的功能编程特性。这些是 Ruby 表达性和优雅风格的基础。
块
基本块语法
# 使用 do...end 的块(多行)
[1, 2, 3].each do |num|
puts num * 2
end
# 使用 {...} 的块(单行)
[1, 2, 3].each { |num| puts num * 2 }
向块让步
def repeat(times)
times.times do
yield # 执行块
end
end
repeat(3) { puts "Hello" }
# 带块参数
def greet
yield("World")
end
greet { |name| puts "Hello, #{name}!" }
块参数
def process_data(data)
result = yield(data)
puts "Result: #{result}"
end
process_data(10) { |x| x * 2 } # 结果: 20
检查块是否存在
def optional_block
if block_given?
yield
else
puts "未提供块"
end
end
optional_block { puts "块已执行" }
optional_block
块局部变量
x = 10
[1, 2, 3].each do |num; local_var|
local_var = num * 2 # local_var 仅存在于块中
puts local_var
end
puts x # 10(未改变)
Proc
创建 Proc
# 使用 Proc.new
my_proc = Proc.new { |x| x * 2 }
puts my_proc.call(5) # 10
# 使用 proc 方法(某些版本中已弃用)
my_proc = proc { |x| x * 2 }
# 使用 ->(Proc 的刺刀 lambda 语法)
my_proc = ->(x) { x * 2 }
Proc 特性
# Proc 不关心参数数量
flexible_proc = Proc.new { |x, y| "x: #{x}, y: #{y}" }
puts flexible_proc.call(1) # x: 1, y:
puts flexible_proc.call(1, 2, 3) # x: 1, y: 2(忽略额外参数)
# Proc 从包含方法返回
def proc_return
my_proc = Proc.new { return "来自 proc" }
my_proc.call
"proc 之后" # 从未到达
end
puts proc_return # "来自 proc"
将 Proc 作为参数传递
def execute_proc(my_proc)
my_proc.call
end
greeting = Proc.new { puts "来自 proc 的问候!" }
execute_proc(greeting)
将块转换为 Proc
def method_with_proc(&block)
block.call
end
method_with_proc { puts "块已转换为 proc" }
Lambda
创建 Lambda
# 使用 lambda 关键字
my_lambda = lambda { |x| x * 2 }
# 使用 ->(刺刀 lambda)
my_lambda = ->(x) { x * 2 }
# 多行刺刀 lambda
my_lambda = ->(x) do
result = x * 2
result + 1
end
puts my_lambda.call(5) # 11
Lambda 特性
# Lambda 强制执行参数数量
strict_lambda = ->(x, y) { x + y }
# strict_lambda.call(1) # ArgumentError
strict_lambda.call(1, 2) # 有效
# Lambda 返回给调用者
def lambda_return
my_lambda = -> { return "来自 lambda" }
my_lambda.call
"lambda 之后" # 这会被到达
end
puts lambda_return # "lambda 之后"
带多个参数的 Lambda
add = ->(x, y) { x + y }
multiply = ->(x, y, z) { x * y * z }
puts add.call(3, 4) # 7
puts multiply.call(2, 3, 4) # 24
# 默认参数
greet = ->(name = "World") { "Hello, #{name}!" }
puts greet.call # "Hello, World!"
puts greet.call("Ruby") # "Hello, Ruby!"
Proc 与 Lambda 对比
# 参数处理
my_proc = Proc.new { |x, y| puts "x: #{x}, y: #{y}" }
my_lambda = ->(x, y) { puts "x: #{x}, y: #{y}" }
my_proc.call(1) # 有效: x: 1, y:
# my_lambda.call(1) # ArgumentError
# 返回行为
def test_return
proc_test = Proc.new { return "proc 返回" }
lambda_test = -> { return "lambda 返回" }
proc_test.call # 从方法返回
"结束" # 从未到达
end
def test_lambda
lambda_test = -> { return "lambda 返回" }
lambda_test.call # 从 lambda 返回
"结束" # 这会被到达
end
# 检查是否为 lambda
my_proc = Proc.new { }
my_lambda = -> { }
puts my_proc.lambda? # false
puts my_lambda.lambda? # true
闭包
def multiplier(factor)
->(x) { x * factor }
end
times_two = multiplier(2)
times_three = multiplier(3)
puts times_two.call(5) # 10
puts times_three.call(5) # 15
# 闭包捕获变量
def counter
count = 0
increment = -> { count += 1 }
decrement = -> { count -= 1 }
value = -> { count }
[increment, decrement, value]
end
inc, dec, val = counter
inc.call
inc.call
puts val.call # 2
dec.call
puts val.call # 1
方法对象
class Calculator
def add(x, y)
x + y
end
end
calc = Calculator.new
add_method = calc.method(:add)
puts add_method.call(3, 4) # 7
# 将方法转换为 proc
add_proc = calc.method(:add).to_proc
puts add_proc.call(5, 6) # 11
符号到 Proc
# & 将符号转换为 proc
numbers = [1, 2, 3, 4, 5]
# 这些是等价的:
numbers.map { |n| n.to_s }
numbers.map(&:to_s)
# 适用于任何方法
["hello", "world"].map(&:upcase) # ["HELLO", "WORLD"]
[1, 2, 3].select(&:even?) # [2]
高阶函数
def compose(f, g)
->(x) { f.call(g.call(x)) }
end
double = ->(x) { x * 2 }
square = ->(x) { x * x }
double_then_square = compose(square, double)
puts double_then_square.call(3) # 36 (3 * 2 = 6, 6 * 6 = 36)
柯里化
# 手动柯里化
add = ->(x) { ->(y) { x + y } }
add_five = add.call(5)
puts add_five.call(3) # 8
# 内置柯里化
multiply = ->(x, y, z) { x * y * z }
curried = multiply.curry
times_two = curried.call(2)
times_two_three = times_two.call(3)
puts times_two_three.call(4) # 24
# 部分应用
puts curried.call(2, 3).call(4) # 24
实用模式
惰性求值
def lazy_value
puts "计算昂贵值..."
42
end
# 包装在 lambda 中以进行惰性求值
lazy = -> { lazy_value }
puts "调用前"
result = lazy.call # 仅在此处计算
puts result
回调模式
class Button
def initialize
@on_click = []
end
def on_click(&block)
@on_click << block
end
def click
@on_click.each(&:call)
end
end
button = Button.new
button.on_click { puts "按钮已点击!" }
button.on_click { puts "另一个处理器" }
button.click
策略模式
class Sorter
def initialize(strategy)
@strategy = strategy
end
def sort(array)
@strategy.call(array)
end
end
ascending = ->(arr) { arr.sort }
descending = ->(arr) { arr.sort.reverse }
sorter = Sorter.new(ascending)
puts sorter.sort([3, 1, 2]) # [1, 2, 3]
sorter = Sorter.new(descending)
puts sorter.sort([3, 1, 2]) # [3, 2, 1]
记忆化
def memoize(&block)
cache = {}
->(arg) do
cache[arg] ||= block.call(arg)
end
end
expensive_operation = memoize do |n|
puts "为 #{n} 计算..."
n * n
end
puts expensive_operation.call(5) # 为 5 计算... 25
puts expensive_operation.call(5) # 25(已缓存)
最佳实践
- 对简单迭代和一次性闭包使用块
- 对严格的参数检查和可返回闭包使用 lambda
- 对灵活的参数处理使用 proc(罕见情况)
- 优先使用 -> 语法(更简洁)
- 对集合的简单方法调用使用 &:symbol
- 利用闭包进行封装和数据隐私
- 在向可选块让步前使用 block_given?
反模式
❌ 不要使用 Proc.new 实现严格行为 - 改用 lambda ❌ 不要忽略返回行为 - 理解 proc 与 lambda 的区别 ❌ 不要过度使用闭包 - 可能导致内存泄漏 ❌ 不要创建深度嵌套的 lambda - 难以阅读和调试 ❌ 不要忘记处理缺失的块 - 用 block_given? 检查
相关技能
- ruby-oop - 用于理解方法上下文
- ruby-metaprogramming - 用于动态块/proc 使用
- ruby-standard-library - 用于使用块的 Enumerable 方法