我在写递归函数。如果有一种好的方法来判断方法的当前运行是否在递归堆栈中,那就太好了。就像这样:
def mymethod if yourcodehere # do stuff end mymethod() end
有什么想法吗?如果有好的方法,我会为它写一个Ruby宝石。
fxnxkyjh1#
您可以使用caller_locations:
caller_locations
def mymethod(x) first, *stack = caller_locations(0) recursion = stack.any? do |cl| cl.absolute_path == first.absolute_path && cl.label == first.label end p x: x, recursion: recursion mymethod(x + 1) if x < 3 end mymethod(1) # {:x=>1, :recursion=>false} # {:x=>2, :recursion=>true} # {:x=>3, :recursion=>true}
通过传递0的 start 值,caller_locations将返回自己的调用,您可以将其与剩余的调用者位置进行比较以检测递归,即甚至跨多个方法调用。您可以通过传递第二个 limit 参数来限制条目的数量,例如如果您只需要检测直接调用。请注意,在启用tail call优化时,上述方法不起作用,因为它有效地重用了最后一个堆栈帧,这使得每个递归调用看起来都像初始调用。一种更简单的方法是在从自身调用方法时只传递一个可选参数,以显式地指示递归调用,例如:
0
def mymethod(x, recursion: false) p x: x, recursion: recursion mymethod(x + 1, recursion: true) if x < 3 end mymethod(1) # {:x=>1, :recursion=>false} # {:x=>2, :recursion=>true} # {:x=>3, :recursion=>true}
除了调试或检测(无意的)无限循环之外,我会避免让一个方法根据其调用上下文有不同的行为。
kfgdxczn2#
有不止一种方法可以完成您想要的任务,但是确定调用方是否是当前方法是微不足道的。例如,假设原始性能不是您的目标,那么下面的代码就可以正常工作。
def foo puts caller.first.match?(__method__.to_s) ? "tail call" : "first call" sleep 0.1 foo end
这将返回:第一呼叫尾唿叫尾唿叫尾唿叫...因为只有当Kernel#caller包含由Kernel#__method__标识的当前方法时,三元条件才为真。
我不确定当你的堆栈变得更深时,上面的方法是否会有性能损失。如果发生这种情况,您可以尝试将显式的 start 和 length 参数传递给Kernel#caller,或者只是将布尔参数作为tail调用的一部分传递。例如:
def foo recursion=false puts recursion ? "tail call" : "first call" sleep 0.1 foo recursion: true end
这给出了与第一个示例完全相同的结果,但不再需要对堆栈跟踪执行正则表达式匹配或对当前方法的Symbol进行自检。它只是在进行尾调用时传递一个truthy值作为关键字参数,因此这种方法可能更快地进行评估。但是,我并没有费心对这两种方法进行基准测试,因此您的里程可能会有所不同。
2条答案
按热度按时间fxnxkyjh1#
您可以使用
caller_locations
:通过传递
0
的 start 值,caller_locations
将返回自己的调用,您可以将其与剩余的调用者位置进行比较以检测递归,即甚至跨多个方法调用。您可以通过传递第二个 limit 参数来限制条目的数量,例如如果您只需要检测直接调用。请注意,在启用tail call优化时,上述方法不起作用,因为它有效地重用了最后一个堆栈帧,这使得每个递归调用看起来都像初始调用。
一种更简单的方法是在从自身调用方法时只传递一个可选参数,以显式地指示递归调用,例如:
除了调试或检测(无意的)无限循环之外,我会避免让一个方法根据其调用上下文有不同的行为。
kfgdxczn2#
方法名匹配调用方
有不止一种方法可以完成您想要的任务,但是确定调用方是否是当前方法是微不足道的。例如,假设原始性能不是您的目标,那么下面的代码就可以正常工作。
这将返回:
第一呼叫
尾唿叫
尾唿叫
尾唿叫
...
因为只有当Kernel#caller包含由Kernel#__method__标识的当前方法时,三元条件才为真。
向尾部调用传递布尔参数
我不确定当你的堆栈变得更深时,上面的方法是否会有性能损失。如果发生这种情况,您可以尝试将显式的 start 和 length 参数传递给Kernel#caller,或者只是将布尔参数作为tail调用的一部分传递。例如:
这给出了与第一个示例完全相同的结果,但不再需要对堆栈跟踪执行正则表达式匹配或对当前方法的Symbol进行自检。它只是在进行尾调用时传递一个truthy值作为关键字参数,因此这种方法可能更快地进行评估。但是,我并没有费心对这两种方法进行基准测试,因此您的里程可能会有所不同。