2012-06-29 47 views
1

有沒有辦法在ruby中做舊的「錯誤恢復下一個」例程?紅寶石「在錯誤恢復下一個」功能

我已經從其他地方動態地填充了一系列值(從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( '標題2',(值[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] } 

如果類型錯誤,NoMethodError或ZeroDivisionError被拋出表達,他們會被抓住並被忽略。

現在你的原始方法不需要任何救援。


如果你真的想要一個on error resume next,你可能會因修補猴子在內核的raise方法做到這一點,但是這將是一個可怕的想法。

+0

:你能澄清?如果您打算編寫「Publish(topic_name,value)」並且使用「try_publish('topic1',value [0] * 10 + value [1])調用,那麼肯定會在調用該方法之前拋出數字異常? – rw950431

+0

對不起,我原來的答案根本沒用,我現在通過使用塊來傳遞可能失敗的計算來修復它。由於塊在內部開始/解救部分中執行,異常將被捕獲。 –

+0

nanothief-看起來好多了,作爲一個剛剛接觸ruby的perl傢伙,我仍然對代碼塊有所瞭解併產生了() – 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'是一個隱含的開始,所以你可以從'safe_publish'版本中刪除'begin'。 –

0

如果你想多一點小心你在做什麼,以及爲什麼on error resume next,我想你會看到你並不真的需要抑制所有例外。正如其他海報指出的那樣,這將使它很難找到並修復錯誤。

你的問題是,你有一堆從互聯網上刮下的數字,並希望對它們進行一些計算,但有些可能無效或丟失。對於無效/缺失的數字,你想跳過任何將使用這些數字的計算。

一些可能的解決方案:

  1. 預過濾器的數據,並刪除任何東西是不是一個有效的數字。
  2. 把你想要做的每個計算放到它自己的方法中。在方法定義上輸入rescue Exception
  3. 定義數字類的「安全」包裝,它不會引發零除等異常,等等。使用這些包裝進行計算。

的「包裝」可能是這個樣子(不要指望完整的,經過測試的代碼,這僅僅是給你的想法):

# 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

老貝爾傢伙,但一個紅寶石n00b我很努力去理解這個 - 儘管我敢肯定它的優秀代碼!運營商超載總是讓我頭腦混亂。 「@__numeric__」是對父類/函數的某種引用嗎? – rw950431

+0

不,它只是一個包含數字的變量,名稱完全是任意的。請記住,在Ruby中,「+」,「 - 」等操作符只是方法調用。我所做的只是在一個自定義類上定義這些運算符,該自定義類包含一個數字並在嘗試對該數字執行任何操作之前執行一些檢查。 –