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

九、总结 #

本章我们学习了:

  1. 块语法:{}、do…end、块参数
  2. yield:调用块、传递参数、block_given?
  3. Proc:创建、调用、作为参数
  4. 闭包:捕获变量、作用域
  5. 转换:块与Proc互转、&符号

接下来让我们学习Ruby的Lambda!

最后更新:2026-03-27