5

このPythonコードをClojureに実装する方法がわかりません

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を内部に配置したときにこれを実現しました)。

誰かがそれを実装するためのより良いアイデアを持っているなら、私に教えてください。

4

5 に答える 5

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

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
于 2012-08-22T08:15:11.277 に答える
7

これが1つのアプローチです:

(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.

(passing 2toは、最初のラウンドで失敗した場合に2回再試行するようにretry要求し、合計3回試行します。フォームが渡される前に発生するため、3秒が出力されます。finalは例外がスローされたことを意味します。)expr:fooprintlnthrowdoretry; Evaluation aborted.

また、forスニペットからのループについて:

より長い範囲でループしようとすると(たとえば、に置き換え(range 3)(range 10))、出力はにi達した後に終了します3。また、println例外をスローするフォームの前に入力すると、もちろん、渡されたものはすべて出力されます。例外スローフォームの後に発生した場合、printlnプリントアウトはありません。いずれの場合も、最大で3つの呼び出しprintlnが実行されます(反復ごとに例外がスローされると想定しています)。

于 2012-08-22T08:09:55.410 に答える
0
(cond (every? nil? (for [x (range (inc retry)) :while (not @tmp-doc)]
 ...do sth) )                  
;all failed
:else
;at least one success
于 2013-12-26T19:55:01.103 に答える
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"))

これは、ベクトルからランダムに正の数を取得するために最大3回試行し、成功しない場合は例外をスローします。

于 2017-03-07T13:48:54.220 に答える
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)))
于 2021-06-17T11:00:02.833 に答える