更新: この回答は元の質問で提示されたコンテキストで機能しますが(doall
シーケンスを実行し、例外があった場合にどれが認識されたかを判断します)、いくつかの欠陥が含まれており、質問のタイトルで提案されている一般的な使用には適していません. ただし、Michał Marczyk の答えを理解するのに役立つかもしれない理論的 (ただし欠陥のある) 根拠を示しています。その答えを理解するのに苦労している場合、この答えは物事をもう少し分解することで役立つかもしれません. また、遭遇する可能性のあるいくつかの落とし穴も示しています。それ以外の場合は、この回答を無視してください。
LazySeq
を実装しているため、理論的には、これは false を返すIPending
まで連続するテール シーケンスを反復するのと同じくらい簡単なはずです。realized?
(defn successive-tails [s]
(take-while not-empty
(iterate rest s)))
(defn take-realized [s]
(map first
(take-while realized?
(successive-tails s))))
さて、あなたが本当にLazySeq
最初から最後まで100%持っているなら、それはそれです-実現されtake-realized
たアイテムを返します.s
編集:わかりました、そうではありません。これは、例外がスローされる前に実現されたアイテムを特定するために機能します。ただし、Michal Marcyzk が指摘するように、シーケンス内のすべての項目が別のコンテキストで実現されます。
その後、次のようにクリーンアップ ロジックを記述できます。
(try
(dorun connections) ; or doall
(catch ConnectException (close-connections (take-realized connections))))
ただし、Clojure の「遅延」構造の多くは 100% 遅延ではないことに注意してください。たとえば、range
は を返しLazySeq
ますが、ダウンし始めるrest
と に変わりChunkedCons
ます。残念ながら、ChunkedCons
は を実装しておらずIPending
、これを呼び出すとrealized?
例外がスローされます。これを回避するには、任意のシーケンスでaのままになるlazy-seq
a を明示的に構築するために使用できます。LazySeq
LazySeq
(defn lazify [s]
(if (empty? s)
nil
(lazy-seq (cons (first s) (lazify (rest s))))))
編集: Michał Marczyk がコメントで指摘したように、基になるシーケンスが遅延して消費されることを保証するものでlazify
はありません。実際、以前は実現されていなかったアイテムが実現される可能性があります (ただし、例外は初回のみスローされるようです)。その唯一の目的は、呼び出しのrest
結果が か のいずれnil
かになることを保証することLazySeq
です。つまり、以下の例を実行するには十分に機能しますが、YMMV.
ここで、とクリーンアップ コードの両方で同じ「遅延」シーケンスを使用すると、 を使用できるようになります。実現中に例外が発生した場合に、部分的なシーケンス (失敗の前の部分) を返す式を作成する方法を示す例を次に示します。dorun
take-realize
(let [v (for [i (lazify (range 100))]
(if (= i 10)
(throw (new RuntimeException "Boo!"))
i))]
(try
(doall v)
(catch Exception _ (take-realized v))))