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
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 :foo
s 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).
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
(cond (every? nil? (for [x (range (inc retry)) :while (not @tmp-doc)]
...do sth) )
;all failed
:else
;at least one success
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.