2009-12-10 14 views
37

अपवादों को छोड़ने से पहले मैं कई बार एक func निष्पादित करने की कोशिश कर रहा हूं। लेकिन क्लोजर में कैच ब्लॉक से पुनरावृत्ति करने के लिए यह वैध नहीं है। यह कैसे प्राप्त किया जा सकता है?क्लोजर: अपवाद पर पुनरावृत्ति कैसे करें?

(loop [tries 10] 
    (try 
    (might-throw-exception) 
    (catch Exception e 
     (when (pos? tries) (recur (dec tries)))))) 

java.lang.UnsupportedOperationException: Cannot recur from catch/finally 
सबसे अच्छा मैं मिल सकता है

निम्नलिखित अनाड़ी समाधान (समारोह में लपेटकर और यह बुला)

(defn do-it [] 
    (try 
    (might-throw-exception) 
    (catch Exception e nil))) 

(loop [times 10] 
    (when (and (nil? (do-it)) (pos? times)) 
    (recur (dec times)))) 

उत्तर

41

मैक्रो बुला रहे हैं ... है

कैसे इस बारे में:

(defn try-times* 
    "Executes thunk. If an exception is thrown, will retry. At most n retries 
    are done. If still some exception is thrown it is bubbled upwards in 
    the call chain." 
    [n thunk] 
    (loop [n n] 
    (if-let [result (try 
         [(thunk)] 
         (catch Exception e 
         (when (zero? n) 
          (throw e))))] 
     (result 0) 
     (recur (dec n))))) 

(defmacro try-times 
    "Executes body. If an exception is thrown, will retry. At most n retries 
    are done. If still some exception is thrown it is bubbled upwards in 
    the call chain." 
    [n & body] 
    `(try-times* ~n (fn [] [email protected])))
+0

यह वह जगह है एक अच्छा समाधान। मैं इसे clojure.contrib या कुछ में जोड़ दूंगा। – GabiMe

+0

वास्तव में वही समाधान है जैसा कि पोस्टर ने सुझाव दिया था। लेकिन मैक्रोज़ सामान्य मामले में करना आसान बनाता है। मैक्रोज़ किसी भी लिस्पी संस्करण की हत्यारा सुविधा है। –

+0

यह बिल्कुल वही समाधान नहीं है। पोस्टर का सुझाव ब्लॉक के रिटर्न वैल्यू को नहीं पकड़ता है, और अगर ऐसा होता है तो ब्लॉक शून्य वापस नहीं कर पाएगा। इसके अलावा अपवाद निगल गए हैं। लेकिन आप सही हैं: यह मूल रूप से एक ही विचार है। मैक्रोज़ बस बॉयलरप्लेट को छुपाएं। – kotarak

12

कोटरक का विचार जाने का तरीका है, लेकिन इस सवाल ने मेरी कल्पना को गुदगुदी कर दिया ताकि मैं एक ही विषय पर एक रिफ प्रदान करना चाहूंगा मैं पसंद करते हैं क्योंकि यह पाश/पुनरावृत्ति होना का उपयोग नहीं करता:

(defn try-times* [thunk times] 
    (let [res (first (drop-while #{::fail} 
           (repeatedly times 
              #(try (thunk) 
               (catch Throwable _ ::fail)))))] 
    (when-not (= ::fail res) 
     res))) 

और कोशिश बार मैक्रो वैसा ही छोड़ दें।

यदि आप थंक को वापस लौटने की अनुमति देना चाहते हैं, तो आप चलो/जब जोड़ी छोड़ सकते हैं, और चलो :: असफल "फ़ंक्शन असफल एन बार" का प्रतिनिधित्व करते हैं, जबकि नील का अर्थ है "फ़ंक्शन शून्य वापस आ गया"। यह व्यवहार और अधिक लचीला लेकिन कम सुविधाजनक हो सकता है (फोन करने वाले के लिए :: यदि यह सिर्फ शून्य के बजाय काम को देखने के लिए असफल जाँच करने के लिए है), इसलिए शायद यह सबसे अच्छा एक वैकल्पिक दूसरे पैरामीटर के रूप में लागू किया जाएगा:

(defn try-times* [thunk n & fail-value] 
    (first (drop-while #{fail-value} ...))) 
+0

+1 के झूठे परिणाम के रूप में माना जाएगा। – rplevy

+1

शायद, यदि आप त्रुटि में से एक (थ्रोबल के वंशज) में से एक प्राप्त करते हैं, तो आप पुनः प्रयास नहीं करना चाहते हैं ... – oshyshko

3

मेरा प्रस्ताव है कि:

(defmacro try-times 
    "Retries expr for times times, 
    then throws exception or returns evaluated value of expr" 
    [times & expr] 
    `(loop [err# (dec ~times)] 
    (let [[result# no-retry#] (try [(do [email protected]) true] 
        (catch Exception e# 
        (when (zero? err#) 
         (throw e#)) 
        [nil false]))] 
     (if no-retry# 
     result# 
     (recur (dec err#)))))) 

"यहाँ कोई त्रुटि" प्रिंट होगा एक बार:

(try-times 3 (println "no errors here") 42) 

प्रिंट होगा "की कोशिश कर रहा" 3 बार, फिर शून्य से विभाजित फेंक:

(try-times 3 (println "trying") (/ 1 0)) 
0

एक और समाधान, बिना मैक्रो

(defn retry [& {:keys [fun waits ex-handler] 
       :or {ex-handler #(log/error (.getMessage %))}}] 
    (fn [ctx] 
    (loop [[time & rem] waits] 
     (let [{:keys [res ex]} (try 
           {:res (fun ctx)} 
           (catch Exception e 
           (when ex-handler 
            (ex-handler e)) 
           {:ex e}))] 
     (if-not ex 
      res 
      (do 
      (Thread/sleep time) 
      (if (seq rem) 
       (recur rem) 
       (throw ex)))))))) 
1

एक try-times मैक्रो सुरुचिपूर्ण है, लेकिन एक बंद के लिए, बस try ब्लॉक से बाहर अपने when खींच:

(loop [tries 10] 
    (when (try 
      (might-throw-exception) 
      false ; so 'when' is false, whatever 'might-throw-exception' returned 
      (catch Exception e 
      (pos? tries))) 
    (recur (dec tries)))) 
संबंधित मुद्दे