retrying something 3 times before throwing an exception - in clojure

后端 未结 4 1295
旧时难觅i
旧时难觅i 2021-02-10 04:58

I don\'t know how to implement this piece of Python code in Clojure

for i in range(3):
    try:
        ......
    except e:
        if i == 2:
            raise         


        
相关标签:
4条回答
  • 2021-02-10 05:29

    Here's one approach:

    (defmacro retry
      "Evaluates expr up to cnt + 1 times, retrying if an exception
      is thrown. If an exception is thrown on the final attempt, it
      is allowed to bubble up."
      [cnt expr]
      (letfn [(go [cnt]
                (if (zero? cnt)
                  expr
                  `(try ~expr
                        (catch Exception e#
                          (retry ~(dec cnt) ~expr)))))]
        (go cnt)))
    

    Example from the REPL:

    user> (retry 2 (do (println :foo) (throw (RuntimeException. "foo"))))
    :foo
    :foo
    :foo
    ; Evaluation aborted.
    

    (Passing 2 to retry asks expr to be retried twice it if fails the first time round, for a total of three attempts. Three :foos are printed, because the println occurs before the throw in the do form passed to retry. The final ; Evaluation aborted. means an exception was thrown.)

    Also, about the for loop from your snippet:

    If you try looping over a longer range (replace (range 3) with (range 10), say), the output will end after i reaches 3. Also, if you put in a println before the form which throws the exception, it will of course print out whatever you pass to it; if the println occurs after the exception-throwing form, there will be no printout. In any case, at most three calls to println will be executed (assuming an exception is thrown on every iteration).

    0 讨论(0)
  • 2021-02-10 05:30

    Similar to Marcyk's answer, but no macro trickery:

    (defn retry
      [retries f & args]
      (let [res (try {:value (apply f args)}
                     (catch Exception e
                       (if (zero? retries)
                         (throw e)
                         {:exception e})))]
        (if (:exception res)
          (recur (dec retries) f args)
          (:value res))))
    

    Slightly complicated because you can't recur inside a catch clause. Note that this takes a function:

    (retry 3 (fn [] 
              (println "foo") 
              (if (zero? (rand-int 2))
                  (throw (Exception. "foo"))
                  2)))
    =>
    foo ;; one or two or three of these
    foo
    2
    
    0 讨论(0)
  • 2021-02-10 05:32
    (cond (every? nil? (for [x (range (inc retry)) :while (not @tmp-doc)]
     ...do sth) )                  
    ;all failed
    :else
    ;at least one success
    
    0 讨论(0)
  • 2021-02-10 05:34

    You can do like this:

    (defn retry
      "Tries at most n times, return first try satisfying pred or nil"
      [times pred? lazy-seq]
      (let [successful-trial (drop-while (complement pred?) (take times lazy-seq))]
        (if (empty? successful-trial)
            nil
            (first successful-trial))))
    

    Then you could use the function as such:

    (when-not (retry 3 pos? (repeatedly #(rand-nth [-1 -2 -3 2 1]))
        (throw (Exception. "my exception message"))
    

    This would try at most three times to take a positive number at random from the vector and if it doesn't succeed, throws an exception.

    0 讨论(0)
提交回复
热议问题