2012-06-29 4 views
1

Есть ли способ выполнить старую «on error resume next» в рубине?ruby ​​"on error resume next" function

У меня есть массив значений, динамически заполняемый из других источников (читайте из тем 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 

Если расчет не удается по какой-либо причине программа должна просто пропустить этот шаг и идти дальше.

Это работает, но, безусловно, лучший способ, чем все те же самые начинающие ... спасательные блоки? Ruby о «DRY» в конце концов ..

Есть ли способ переписать вышеприведенное так, чтобы была использована единственная конструкция начала .rescue, все еще позволяющая делать все расчеты?

ОБНОВЛЕНО

Как безопасно сделать что-то вроде

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

и коллировать с safe_Publish ('topic2', (значение [3]/значение [4] спасательное ноль))

Основная проблема заключается в том, что вышеупомянутый ловит ВСЕ исключения, а не только те, которые я ожидаю, что заставляет меня немного нервничать.

+1

Вы из vb? – texasbruce

ответ

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 в ядре, но это было бы ужасной идеей.

+0

: Можете ли вы уточнить? Если вы намеревались написать «Опубликовать (topic_name, value)» и быть вызваны с помощью «try_publish» («topic1», значение [0] * 10 + значение [1]), то, конечно, числовое исключение бросается до вызова метода? – rw950431

+0

Извините, мой первоначальный ответ не был вообще полезен. Я исправил его сейчас, используя блок, чтобы передать вычисления, которые могут быть неудачными. Поскольку блок выполняется во внутреннем разделе «Начало/Спасение», исключения будут обнаружены. –

+0

nanothief-, который выглядит намного лучше. Будучи перловым парнем, который новичок в рубине, я все еще получаю голову от кодовых блоков и yield() – rw950431

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 
+0

Это будет работать нормально, если вы хотите получить одинаковые вычисления для каждого значения, а не для разных вычислений. – rw950431

+0

Хорошо, я обновил ответ, чтобы обрабатывать различные вычисления. –

+0

'def' - это неявное начало, поэтому вы можете удалить' begin' из своей версии 'safe_publish'. –

0

Если вы считаете, немного более тщательно о том, что вы делаете, и почему вы хотите on error resume next, я думаю, вы увидите, что вы на самом деле не нужно, чтобы подавить все исключения. Как указывали другие плакаты, это затрудняло бы поиск и исправление ошибок.

Ваша проблема в том, что у вас есть куча чисел, очищенных от Интернета, и вы хотите выполнить некоторые вычисления на них, но некоторые из них могут быть недействительными или отсутствовать. Для недопустимых/отсутствующих номеров вы хотите пропустить любые вычисления, которые будут использовать эти числа.

Несколько возможных решения:

  1. фильтра предварительной очистки данных и удалите все, что не является допустимым числом.
  2. Поместите каждый расчет, который вы хотите сделать, в свой собственный метод. Поместите метод rescue Exception на определение метода.
  3. Определите «безопасные» обертки для числовых классов, которые не вызывают исключения при делении на ноль и т. Д. Используйте эти обертки для ваших вычислений.

The «оберток» может выглядеть примерно так (не ожидают завершения испытания код, это просто чтобы дать вам идею):

# 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 
+0

2. В основном, что я делаю сейчас без вызова метода. Можете ли вы разместить образец кода для 3.? – rw950431

+0

Будучи старый парень perl, но ruby n00b Я изо всех сил пытаюсь понять это, хотя я уверен, что это отличный код! Перегрузка оператора всегда путалась с моей головой. «@__numeric__» - это какая-то ссылка на родительский класс/функцию? – rw950431

+0

Нет, это просто переменная, которая содержит число, и имя полностью произвольно. Помните, что в Ruby «операторы», такие как +, - и т. Д., Являются просто вызовами метода. Все, что я делаю, это определение этих операторов в пользовательском классе, который содержит число и выполняет некоторые проверки перед попыткой выполнить любые операции над номером. –

Смежные вопросы