在抛出异常之前重试了3次 - 在clojure中

时间:2012-08-22 07:56:34

标签: clojure

我不知道如何在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时我意识到了这一点。)

如果有人有更好的想法实现这一点,请赐教。

5 个答案:

答案 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)))