红宝石“错误恢复下一个”功能

Posted

技术标签:

【中文标题】红宝石“错误恢复下一个”功能【英文标题】:ruby "on error resume next" function 【发布时间】:2012-06-29 05:21:28 【问题描述】:

有没有办法在 ruby​​ 中执行旧的“on error 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 是关于“DRY”的。

有没有办法重写上面的内容,以便使用单个 begin..rescue 构造,同时仍然允许尝试所有计算?

更新

做这样的事情有多安全

def safe_Publish(topic,value)
  return if value.nil?
  Publish(topic,value)
end

并调用 safe_Publish('topic2',(value[3]/value[4] rescue nil))

主要问题是上面捕获了所有异常,而不仅仅是我期望的异常,这让我有点紧张。

【问题讨论】:

【参考方案1】:

on error resume next 编码风格真的很危险——因为它让你很难找到你不小心引入程序的新错误。相反,我会编写一个不会引发这些异常的不同版本的发布:

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 方法来实现,但那将是一个可怕的想法。

【讨论】:

:你能澄清一下吗?如果您打算编写“Publish(topic_name,value)”并使用“try_publish('topic1',value[0]*10+value[1])”调用,那么在调用该方法之前肯定会抛出数字异常吗? 对不起,我原来的答案一点用都没有。我现在通过使用一个块来传递可能失败的计算来修复它。由于块在内部开始/救援部分执行,异常将被捕获。 nanothief- 看起来好多了。作为一个刚接触 ruby​​ 的 perl 人,我仍在思考代码块和 yield() 向基本上提出相同解决方案的其他人道歉:您的意见同样有价值,但我只能选择一个“接受的答案”。我已经学会了代码块的力量,谢谢大家!【参考方案2】:

如果你仔细想想你在做什么,为什么你想要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

【讨论】:

2.基本上是我现在在没有方法调用的情况下正在做的事情。您可以发布 3. 的示例代码吗? 作为一个 perl 的老家伙,但是一个 ruby​​ n00b 我很难理解其中的大部分内容——尽管我确信它的代码很棒!运算符重载总是让我头疼。 “@__numeric__”是对父类/函数的某种引用吗? 不,它只是一个包含数字的变量,名称完全是任意的。请记住,在 Ruby 中,像 +、- 等“运算符”只是方法调用。我所做的只是在自定义类上定义这些运算符,该类包含一个数字并在尝试对数字执行任何操作之前进行一些检查。【参考方案3】:

这显示了如何将一堆快速操作包装到一个循环中,每个操作都受到开始/救援的保护:

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

【讨论】:

如果您希望对每个值进行相同的计算,而不是不同的计算,那将可以正常工作 def 是隐式开头,因此您可以从您的safe_publish 版本中删除begin 我说得对吗 -> 类似于 lambda 即匿名函数?仍在尝试掌握代码块、lambdas 和 Procs,如果有人有好的指导,我将不胜感激

以上是关于红宝石“错误恢复下一个”功能的主要内容,如果未能解决你的问题,请参考以下文章

有人可以推荐一个好的宝石来处理联系人吗? [关闭]

红宝书第8章.BOM

奇葩app大盘点,你知道几个

scratch寻找宝石 少儿编程电子学会图形化编程scratch等级考试二级真题和答案解析2021-3

如何从最后一个元素开始遍历数组? (红宝石)

红宝石。 (健康)状况) ?返回:下一个。返回错误