如何让递归二叉树前序遍历方法复用初始传入的代码块?
递归二叉树前序遍历的代码块复用问题
我正在实现一个递归的二叉树深度优先前序遍历方法,希望传入代码块时也能正常工作,但内部递归调用没法复用初始传入的代码块。当前代码如下:
def pre_order(node = @root, values = []) return values if node.nil? block_given? ? values << yield(node.value) : values << node.value pre_order(node.left, values) if node.left pre_order(node.right, values) if node.right values end
不带代码块调用#pre_order时返回正确结果:[7, 1, 4, 65, 13, 97],但传入代码块(比如#pre_order { |value| value * 2 })时,返回结果[14, 1, 4, 65, 13, 97],和期望的[14, 2, 8, 130, 26, 194]不符。我觉得应该把初始代码块存成变量,让递归调用复用它,请问怎么用Ruby风格实现?
解决方案
核心问题是递归调用时没有传递初始的代码块,导致后续递归里block_given?返回false,直接把原始值加入数组。Ruby里可以通过&block参数捕获代码块,再在递归时传递这个块来解决:
修改后的代码
def pre_order(node = @root, values = [], &block) return values if node.nil? # 根据是否存在代码块处理节点值 processed_value = block ? block.call(node.value) : node.value values << processed_value # 递归调用时传递捕获到的代码块 pre_order(node.left, values, &block) if node.left pre_order(node.right, values, &block) if node.right values end
关键说明
- 捕获代码块:方法参数里添加
&block,初始调用传入的代码块会被自动转换成Proc对象保存到block变量中,同时兼容不带块的调用(此时block为nil)。 - 替换
yield:用block.call(node.value)替代yield——yield依赖当前方法上下文的代码块,递归调用时没传块的话就会失效,而直接调用Proc对象能确保复用初始的代码逻辑。 - 传递代码块:递归调用时加上
&block,把捕获到的Proc重新转换成代码块传递给下一层,保证每一层遍历都能执行同一个代码块。
这样调用pre_order { |v| v*2 }就能得到期望的[14, 2, 8, 130, 26, 194],不带块的调用也能正常返回原始值数组。
另一种实现方式(兼容老写法)
如果想保留初始参数结构,也可以在第一次调用时用Proc.new捕获代码块,后续递归直接传递这个Proc变量:
def pre_order(node = @root, values = [], block = nil) # 初始调用时捕获代码块,默认返回原数值 block ||= Proc.new { |v| v } if block_given? return values if node.nil? values << (block ? block.call(node.value) : node.value) pre_order(node.left, values, block) if node.left pre_order(node.right, values, block) if node.right values end
不过第一种用&block的写法更符合Ruby的惯用风格,简洁直观。
内容的提问来源于stack exchange,提问作者jbk
相关产品推荐
相关产品推荐

