我不知道如何在Clojure中实现这段Python代码
for i in range(3):
try:
......
except e:
if i == 2:
raise e
else:
continue
else:
break
我想知道为什么Python中这么简单的东西在Clojure中如此难以理解。我认为困难在于因为Clojure是一种函数式编程语言,因此不适合这样的命令式任务。这是我的尝试:
(first
(remove #(instance? Exception %)
(for [i (range 3)]
(try (......)
(catch Exception e
(if (== i 2)
(throw e)
e)))))))
这非常难看,更糟糕的是,它没有按预期工作。 for循环实际上是完全被评估而不是懒惰(当我在其中放入println时我意识到了这一点。)
如果有人有更好的想法实现这一点,请赐教。
答案 0 :(得分:11)
与Marcyk的答案类似,但没有宏观诡计:
(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))))
稍微复杂一点,因为recur
子句中不能catch
。请注意,这需要一个函数:
(retry 3 (fn []
(println "foo")
(if (zero? (rand-int 2))
(throw (Exception. "foo"))
2)))
=>
foo ;; one or two or three of these
foo
2
答案 1 :(得分:7)
这是一种方法:
(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)))
REPL的例子:
user> (retry 2 (do (println :foo) (throw (RuntimeException. "foo"))))
:foo
:foo
:foo
; Evaluation aborted.
(将2
传递给retry
要求expr
重试两次,如果第一次失败,则总共三次尝试。三个:foo
被打印,因为println
发生在throw
表单传递给do
之前retry
。最终; Evaluation aborted.
表示抛出异常。)
另外,关于代码段中的for
循环:
如果您尝试在更长的范围内进行循环(将(range 3)
替换为(range 10)
,请说),输出将在i
到达3
后结束。此外,如果您在抛出异常的表单之前放入println
,它当然会打印出您传递给它的任何内容;如果在抛出异常表格后发生println
,则不会打印输出。在任何情况下,最多会执行三次println
调用(假设每次迭代都会抛出异常)。
答案 2 :(得分:0)
(cond (every? nil? (for [x (range (inc retry)) :while (not @tmp-doc)]
...do sth) )
;all failed
:else
;at least one success
答案 3 :(得分:0)
你可以这样做:
(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))))
然后你可以这样使用这个功能:
(when-not (retry 3 pos? (repeatedly #(rand-nth [-1 -2 -3 2 1]))
(throw (Exception. "my exception message"))
这最多可以尝试三次从向量中随机取一个正数,如果它没有成功,则抛出一个异常。
答案 4 :(得分:0)
根据这个问题的答案重试睡眠和另一个实现:
(defn retry [retries sleep-ms func url options]
(let [{:keys [status headers body error] :as resp} @(func url options)]
(if error
(do
(log/error (str "Retry " retries " returned error: " e))
(Thread/sleep sleep-ms)
(if (= retries 1)
resp
(retry (dec retries) sleep-ms func url options)))
resp)))