Ruby带块方法返回值异常疑问及优雅赋值方案咨询
Ruby中String#each_char带块调用的常见疑问解答
先还原你遇到的场景:在Ruby 2.6.3中,查看String#each_char的API文档时,示例标注为each_char {|cstr| block } → str,但实际在irb中调用时,返回的是原字符串而非块内处理的结果:
irb(main):001:0> v = "foobar".each_char { |c| c + "." } => "foobar" irb(main):002:0> puts v foobar => nil
只有用break指令,才能从块中返回值并赋值:
irb(main):003:0> v = "foobar".each_char { |c| break c + "." } => "f."
下面逐个解答你的问题:
1. 为何带块调用的方法不返回其计算的结果?
这是Ruby迭代器方法的核心设计惯例:所有以each开头的迭代器,核心职责是遍历集合(字符串、数组、哈希等)的每个元素,把元素传递给块执行逻辑,但方法本身的返回值默认是原集合对象,而非块的执行结果。
块在这里的定位是“执行副作用逻辑”——比如打印元素、修改外部变量、写入文件等,迭代器不会自动收集块的返回值,除非方法本身被设计成这样(比如map、collect这类专门做转换的方法)。
2. 这是String#each_char的特殊实现吗?我确认Array#each会返回新数组,但String#each_byte也不返回处理结果,这是怎么回事?
完全不是each_char的特殊实现,而是所有each系列方法的统一行为!你应该是把Array#each和Array#map搞混了:
Array#each同样返回原数组,而非新数组:irb(main):001:0> arr = [1,2,3].each { |num| num * 2 } => [1,2,3]- 而
Array#map(或collect)才是专门用来收集块返回值、生成新数组的方法:irb(main):001:0> arr = [1,2,3].map { |num| num * 2 } => [2,4,6]
String#each_byte和each_char同属each家族,自然遵循相同规则:遍历元素给块执行,返回原字符串。
3. 从块中获取处理后的值并赋值的最优雅方式是什么?猴子补丁是否可行?
最优雅的方式是用Ruby提供的专门用于集合转换的方法,而非强行改造each:
- 如果需要将每个字符处理后拼接成新字符串,可以用
each_char配合map+join(Ruby 2.6+支持字符串直接用map,也可以先用chars转成字符数组):# 写法1 irb(main):001:0> v = "foobar".each_char.map { |c| c + "." }.join => "f.o.o.b.a.r." # 写法2(更简洁) irb(main):001:0> v = "foobar".chars.map { |c| "#{c}." }.join - 如果只需要块中的某个特定值(比如第一个字符的处理结果),直接取元素处理比用
break更清晰:irb(main):001:0> v = "foobar".chars.first + "." => "f."
至于猴子补丁,非常不推荐。修改Ruby核心类(比如String)的原生方法会破坏代码的可维护性和兼容性——其他开发者会默认each_char遵循Ruby原生行为,突然改变它的返回值很容易引发难以排查的bug。如果确实需要类似功能,建议自己封装工具方法,或者用模块扩展的方式,而非直接修改核心类。
内容的提问来源于stack exchange,提问作者D. Asyl
相关产品推荐
相关产品推荐

