You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby中yield在代码块内的工作原理及相关疑问解答

Ruby代码中yield与自定义Enumerable的运行逻辑解析

原代码与输出

module MyEnumerable
  def my_select
    new_array = []
    each do |value|
      new_array << value if yield(value)
    end
    new_array
  end
end

class Week
  include MyEnumerable

  def initialize
    @days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
  end
  def each
    @days.each { |day| yield day }
  end
end

week = Week.new
puts week.my_select { |day| day.start_with?("T") }

程序输出:

Tuesday
Thursday

困惑代码块

用户对以下Week#each方法的运行逻辑存在疑问:

def each
  @days.each { |day| yield day }
end

核心疑问解答

疑问1:为什么Week#each用了yield却没显式传代码块还能运行?

你看到的Week#each本身没显式声明接收代码块,但**my_select调用each时已经传入了代码块**——就是my_select里的do |value| new_array << value if yield(value) end。Ruby中,方法调用时跟在后面的代码块会自动绑定到被调用方法,无需用&block显式声明,方法内部直接用yield就能调用这个代码块。

疑问2:yield不是应该在接收代码块的方法内使用吗?

完全正确,yield的作用就是在接收了代码块的方法内部调用该代码块。这里Week#each确实接收了代码块(来自my_select的调用),而my_select自己也接收了外层调用时传入的代码块(week.my_select { |day| ... }),两者是不同层级的代码块,对应各自的yield调用。

疑问3:yield在代码块内是如何工作的?

yield的本质是调用当前方法绑定的代码块,并可传递参数:

  • Week#each里的yield day:调用my_select传给它的代码块,把遍历到的day作为参数传给该代码块的value变量。
  • my_select代码块里的yield(value):调用my_select自己收到的外层代码块,把value(即当前day)传给外层代码块的day变量,拿到返回的true/false,以此判断是否将元素加入结果数组。

重构后my_select方法的逻辑解析

重构后的代码仅将do...end替换为{...},逻辑完全一致,核心是两层yield的调用链:

def my_select
  new_array = []
  each { |value| new_array << value if yield(value) }
  new_array
end
  1. 外层调用:week.my_select { |day| day.start_with?("T") },将判断逻辑的代码块传给my_select。
  2. my_select调用each:把内部的处理代码块({ |value| ... })传给Week#each。
  3. Week#each遍历执行:遍历@days数组,每拿到一个day就用yield day调用my_select传入的代码块,将day赋值给value。
  4. 内层代码块执行:调用yield(value)触发外层的判断代码块,根据返回的布尔值决定是否将value加入new_array。
  5. 返回结果:遍历完成后,my_select返回筛选后的new_array。

内容的提问来源于stack exchange,提问作者current_user

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 00:22:37