提问人:Chris B 提问时间:11/22/2014 最后编辑:Chris B 更新时间:11/22/2014 访问量:62
通过引用传递计数器的首选 ruby 习惯用法
Preferred ruby idiom for passing a counter by reference
问:
我有一个长期运行的迭代方法,它很容易基于我无法控制的外部因素出错。我想在调用方的范围内保留成功完成的迭代计数,以便在方法爆炸时,调用方可以使用成功完成的迭代数作为其错误处理和报告功能的一部分。由于 ruby 是严格按值传递的(真的没有必要争论这一点),我的选择似乎仅限于传递一个计数器对象(这似乎太基本了,不值得它自己的对象)或一个人为的计数器数组,例如:
counter = [0]
begin
LongRunningStuff.error_prone_iterations(counter)
puts "success! completed #{counter[0]} iterations"
rescue
puts "failed! completed #{counter[0]} iterations"
end
这感觉很糟糕(充其量),最坏的情况是彻头彻尾的愚蠢。对一些更红宝石的东西有什么建议吗?
答:
3赞
tadman
11/22/2014
#1
如果 Ruby 中有两条规则,那就是一切都是一个对象,每个变量都像是对对象的引用。这可能看起来有点奇怪,但实际上非常一致。在其他混合了基元和对象、引用和值的语言中,往往会有更多的混淆。
与其创建一个没有内在含义的笨拙数组,不如创建一个上下文对象并将其传入呢?例:
context = {
counter: 0
}
SomeModule.method_call(context)
puts context[:counter]
你甚至可以使用像 OpenStruct 这样的东西来整理一下:
require 'openstruct'
context = OpenStruct.new(counter: 0)
SomeModule.method_call(context)
puts context.counter
更“Ruby”的方法是创建一个上下文类,为你正在做的事情提供更多的语义意义:
class CounterContext
attr_reader :counter
def initialize
@counter = 0
end
def count!
@counter += 1
end
end
这样,你就可以进行一些轻量级的抽象,使你的代码更具可读性。这也意味着,如果您想找出谁在触发该方法,而不是尝试跟踪计数器的递增位置,那么使用像这样的方法,您可以轻松地存根一些调试代码。count!
评论
1赞
tadman
11/22/2014
如果你是从 CS 的角度来看的,那么当然。为了清理这个答案,我撤回了我所有的评论,并相应地调整了我的答案。
0赞
Chris B
11/22/2014
感谢您提出的有助于添加上下文的建议。本着友情的精神,我还删除了我的按参考/按值评论。正如你所指出的,我确实“从CS的角度”来对待工作,就像厨师从烹饪的角度来对待他或她的工作一样。:-)
评论