有没有办法在ruby中执行旧的“on next resume next”例程?
我已经从其他地方动态填充了数组(从MQTT主题中读取以确切)然后我想对它们进行一系列数值计算并发布结果。值应该是数字,但可能缺少或非数字。
目前我的代码看起来像
values=[]
//values get loaded here
begin
Publish('topic1',value[0]*10+value[1])
rescue TypeError,NoMethodError,ZeroDivisionError
end
begin
Publish('topic2',value[3]/value[4])
rescue TypeError,NoMethodError,ZeroDivisionError
end
//etc etc
如果计算因任何原因失败,程序应该跳过该步骤继续。
它有效,但肯定比所有那些相同的begin..rescue块更好吗? Ruby毕竟是“干”......
有没有办法重写上面的内容,以便使用单个begin..rescue结构,同时仍允许尝试所有计算?
已更新
像
那样安全def safe_Publish(topic,value)
return if value.nil?
Publish(topic,value)
end
并致电 safe_Publish('topic2',(value [3] / value [4] rescue nil))
主要的问题是上面所有的例外不仅仅是我期待的那些让我有点紧张的例外。
答案 0 :(得分:1)
on error resume next
编码风格非常危险 - 因为它会让你很难找到偶然引入程序的新bug。相反,我只会编写一个不会抛出这些异常的不同版本的发布:
def try_publish(topic_name)
begin
Publish('topic1',yield)
rescue TypeError,NoMethodError,ZeroDivisionError
# are you sure you don't want to do anything here? Even logging the errors
# somewhere could be useful.
end
end
然后你可以用:
来调用它try_publish('topic1') { value[0]*10+value[1] }
如果表达式抛出TypeError,NoMethodError或ZeroDivisionError,它们将被捕获并被忽略。
现在您的原始方法不需要任何救援。
如果你真的想要一个on error resume next
,你可以通过猴子修补内核中的raise
方法来做到这一点,但这可能是一个可怕的想法。
答案 1 :(得分:0)
这显示了如何将一堆快速操作包装成一个循环,每个操作都受到开始/救援的保护:
values = [1,2,3,0,4]
ops = [ ->{values[0]/values[1]}, ->{values[2]/values[3]} ]
ops.each do |op|
begin
puts "answer is #{op.call}"
rescue ZeroDivisionError
puts "cannot divide by zero"
end
end
我更喜欢safe_publish
方法,因为你可以对它进行单元测试,它封装了在一个地方进行安全调用和处理错误的逻辑:
def safe_publish(topic, &block)
begin
value = block.call
publish(topic, value)
rescue
# handle the error
end
end
然后你可以用以下代码调用它:
safe_publish 'topic0' do
value[0]*10+value[1]
end
答案 2 :(得分:0)
如果你仔细考虑一下你正在做什么,为什么你想要on error resume next
,我想你会发现你真的不需要压制所有例外。正如其他海报所指出的那样,这将很难找到并修复错误。
你的问题是你有一堆从互联网上删除的数字,并希望对它们进行一些计算,但有些可能无效或丢失。对于无效/缺失的数字,您希望跳过任何使用这些数字的计算。
一些可能的解决方案:
rescue Exception
。“包装器”可能看起来像这样(不要指望完整的,经过测试的代码;这只是为了给你这个想法):
# This is not designed for "mixed" arithmetic between SafeNumerics and ordinary Numerics,
# but if you want to do mixed arithmetic, that can also be achieved
# more checks will be needed, and it will also need a "coerce" method
class SafeNumeric
attr_reader :__numeric__
def initialize(numeric)
@__numeric__ = numeric.is_a?(String) ? numeric.to_f : numeric
end
def zero?
@__numeric__.zero?
end
def /(other)
if other.zero? || @__numeric__.nil? || other.__numeric__.nil?
SafeNumeric.new(nil) # could use a constant for this to reduce allocations
else
SafeNumeric.new(@__numeric__ / other.__numeric__)
end
end
def to_s; @__numeric__.to_s; end
def inspect; @__numeric__.inspect; end
# methods are also needed for +, -, *
end
然后使用它:
numbers = scraped_from_net.map { |n| SafeNumeric.new(n) }
# now you can do arithmetic on "numbers" at will