ruby“on error resume next”功能

时间:2012-06-29 05:21:29

标签: ruby

有没有办法在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))

主要的问题是上面所有的例外不仅仅是我期待的那些让我有点紧张的例外。

3 个答案:

答案 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,我想你会发现你真的不需要压制所有例外。正如其他海报所指出的那样,这将很难找到并修复错误。

你的问题是你有一堆从互联网上删除的数字,并希望对它们进行一些计算,但有些可能无效或丢失。对于无效/缺失的数字,您希望跳过任何使用这些数字的计算。

一些可能的解决方案:

  1. 预过滤您的数据并删除任何有效数字。
  2. 将您想要执行的每个计算放入自己的方法中。在方法定义上加上rescue Exception
  3. 为数字类定义“安全”包装器,这些包装器不会在除以零之外引发异常,等等。使用这些包装器进行计算。
  4. “包装器”可能看起来像这样(不要指望完整的,经过测试的代码;这只是为了给你这个想法):

    # 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