我不知道如何在Clojure中实现这段Python代码
for i in range(3):
try:
......
except e:
if i == 2:
raise e
else:
continue
else:
break
Run Code Online (Sandbox Code Playgroud)
我想知道为什么Python中这么简单的东西在Clojure中如此之难.我认为困难在于因为Clojure是一种函数式编程语言,因此不适合这样的命令式任务.这是我的尝试:
(first
(remove #(instance? Exception %)
(for [i (range 3)]
(try (......)
(catch Exception e
(if (== i 2)
(throw e)
e)))))))
Run Code Online (Sandbox Code Playgroud)
它非常丑陋,更糟糕的是,它没有按预期工作.for循环实际上是完全评估而不是懒惰(我在内置println时意识到这一点).
如果有人有更好的想法来实现它,请赐教.
Joo*_*aat 12
类似于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))))
Run Code Online (Sandbox Code Playgroud)
稍微复杂,因为你不能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
Run Code Online (Sandbox Code Playgroud)
这是一种方法:
(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)))
Run Code Online (Sandbox Code Playgroud)
REPL的示例:
user> (retry 2 (do (println :foo) (throw (RuntimeException. "foo"))))
:foo
:foo
:foo
; Evaluation aborted.
Run Code Online (Sandbox Code Playgroud)
(传递2到retry询问expr重试两次,如果轮失败第一次,总共三次尝试的.三个:foos的打印,因为println在之前发生throw在do传递给形式retry.最终的; Evaluation aborted.装置抛出异常.)
另外,关于for你的代码片段的循环:
如果您尝试遍历更远的射程(替换(range 3)用(range 10),说),输出将结束之后i到达3.此外,如果您在println抛出异常的表单之前放入一个,它当然会打印出您传递给它的任何内容; 如果在println抛出异常形式之后发生,则不会打印输出.在任何情况下,最多println将执行三次调用(假设每次迭代都抛出异常).