Ruby块与Proc #
一、块概述 #
块是Ruby特有的特性,是一段可以传递给方法的代码。块可以使用 {} 或 do...end 定义。
二、块语法 #
2.1 单行块 #
ruby
[1, 2, 3].each { |n| puts n }
[1, 2, 3].map { |n| n * 2 }
2.2 多行块 #
ruby
[1, 2, 3].each do |n|
doubled = n * 2
puts doubled
end
File.open("file.txt", "w") do |file|
file.puts "Line 1"
file.puts "Line 2"
end
2.3 块参数 #
ruby
[1, 2, 3].each { |n| puts n }
{ a: 1, b: 2 }.each { |k, v| puts "#{k}: #{v}" }
三、yield #
3.1 基本用法 #
ruby
def with_timing
start = Time.now
yield
elapsed = Time.now - start
puts "耗时: #{elapsed}秒"
end
with_timing { sleep 1 }
3.2 传递参数 #
ruby
def twice
yield 1
yield 2
end
twice { |n| puts n }
3.3 获取返回值 #
ruby
def process
result = yield
puts "结果: #{result}"
end
process { 1 + 2 }
3.4 检查块 #
ruby
def maybe_yield
if block_given?
yield
else
"No block given"
end
end
maybe_yield { "Block executed" }
maybe_yield
四、Proc #
4.1 创建Proc #
ruby
my_proc = Proc.new { |n| n * 2 }
my_proc.call(5)
my_proc[5]
my_proc.(5)
my_proc === 5
my_proc = proc { |n| n * 2 }
my_proc.call(5)
4.2 Proc作为参数 #
ruby
def process(value, proc)
proc.call(value)
end
double = Proc.new { |n| n * 2 }
process(5, double)
process(5, Proc.new { |n| n * 2 })
4.3 &符号 #
ruby
def with_block(&block)
block.call
end
with_block { puts "Hello" }
def pass_block_to_method(arr, &block)
arr.map(&block)
end
pass_block_to_method([1, 2, 3]) { |n| n * 2 }
4.4 符号转Proc #
ruby
[1, 2, 3].map(&:to_s)
[1, 2, 3].map { |n| n.to_s }
["a", "b", "c"].map(&:upcase)
[1, 2, 3].map(&:even?)
五、闭包 #
5.1 捕获变量 #
ruby
def create_counter
count = 0
Proc.new { count += 1 }
end
counter = create_counter
counter.call
counter.call
counter.call
5.2 多个闭包共享变量 #
ruby
def create_counters
count = 0
[
Proc.new { count += 1 },
Proc.new { count -= 1 },
Proc.new { count }
]
end
inc, dec, get = create_counters
inc.call
inc.call
get.call
dec.call
get.call
5.3 闭包与作用域 #
ruby
x = 1
my_proc = Proc.new do
x = 2
y = 3
end
my_proc.call
x
y
六、块与Proc转换 #
6.1 块转Proc #
ruby
def capture_block(&block)
block
end
my_proc = capture_block { |n| n * 2 }
my_proc.call(5)
6.2 Proc转块 #
ruby
double = Proc.new { |n| n * 2 }
[1, 2, 3].map(&double)
def apply(value, &block)
block.call(value)
end
apply(5, &double)
6.3 方法转Proc #
ruby
to_s = :to_s.to_proc
to_s.call(42)
[1, 2, 3].map(&:to_s)
七、实用示例 #
7.1 计时器 #
ruby
def benchmark
start = Time.now
result = yield
elapsed = Time.now - start
puts "耗时: #{elapsed.round(4)}秒"
result
end
benchmark { sleep 1 }
7.2 资源管理 #
ruby
def with_file(path, mode = "r")
file = File.open(path, mode)
begin
yield file
ensure
file.close
end
end
with_file("test.txt", "w") do |f|
f.puts "Hello"
end
7.3 重试机制 #
ruby
def with_retry(max_retries = 3)
retries = 0
begin
yield
rescue => e
retries += 1
retry if retries < max_retries
raise e
end
end
with_retry { risky_operation }
7.4 缓存 #
ruby
def memoize(key)
@cache ||= {}
return @cache[key] if @cache.key?(key)
@cache[key] = yield
end
memoize(:expensive_calculation) { expensive_operation }
7.5 回调系统 #
ruby
class EventEmitter
def initialize
@callbacks = {}
end
def on(event, &callback)
@callbacks[event] ||= []
@callbacks[event] << callback
end
def emit(event, *args)
@callbacks[event]&.each { |callback| callback.call(*args) }
end
end
emitter = EventEmitter.new
emitter.on(:click) { |x, y| puts "Clicked at #{x}, #{y}" }
emitter.emit(:click, 100, 200)
八、最佳实践 #
8.1 选择块语法 #
ruby
[1, 2, 3].each { |n| puts n }
File.open("file.txt") do |f|
f.each_line do |line|
process(line)
end
end
8.2 使用block_given? #
ruby
def optional_block
if block_given?
yield
else
default_behavior
end
end
8.3 Proc参数命名 #
ruby
def process(value, &block)
block.call(value) if block
end
def process(value, processor: nil, &block)
processor = processor || block
processor.call(value)
end
九、总结 #
本章我们学习了:
- 块语法:{}、do…end、块参数
- yield:调用块、传递参数、block_given?
- Proc:创建、调用、作为参数
- 闭包:捕获变量、作用域
- 转换:块与Proc互转、&符号
接下来让我们学习Ruby的Lambda!
最后更新:2026-03-27