ショートさせたい遅延シーケンスに対する Clojure のチャンク動作を回避するにはどうすればよいですか?
-
25-09-2019 - |
質問
長くて遅延したシーケンスがあるので、それを削減して遅延テストしたいと考えています。2 つの連続する要素がなくなるとすぐに、 =
(または他の述語) 相互に、作成コストがかかるリストの消費をやめたいと考えています。はい、これは次のように聞こえます take-while
, 、しかし、さらに読んでください。
こんな風にシンプルでエレガントなものを書きたかったのです(ちょっとそう思ったふり) every?
のように機能します reduce
):
(every? = (range 100000000))
しかし、それは遅延的に機能しないため、無限のシーケンスでハングします。これはほぼ希望どおりに機能することがわかりました。
(apply = (range 100000000))
しかし、シーケンスのチャンク化により、余分な不要な要素が作成され、テストされることに気づきました。少なくとも、これは次のコード部分で起こっていることだと思います。
;; Displays chunking behavior in groups of four on my system and prints 1 2 3 4
(apply = (map #(do (println %) %) (iterate inc 1)))
;; This prints 0 to 31
(apply = (map #(do (println %) %) (range)))
を使用して回避策を見つけました take-while
, 、 そして count
取得された要素の数を確認する必要がありますが、それはかなり面倒です。
リッチ・ヒッキーに、いくつかの組み合わせを作るよう丁寧に提案すべきでしょうか? reduce
そして every?
適切に短絡していますか、それともすでに存在する明らかな方法を見逃しているのでしょうか?
編集: 親切な 2 人が、遅延シーケンスのチャンクを回避するためのソリューションを投稿しましたが、実行時にチャンクを回避するにはどうすればよいですか? apply
, 、4 人ずつのグループで分割して消費しているようです。
編集#2: Stuart Sierra が指摘し、私が独自に発見したように、これは実際にはチャンク化されていません。普通に行動しているだけなので、これは終了として、彼に答えを与えます。興味のある方のために、問題の削減部分を実行するための小さな関数を別の回答に含めました。
解決
2回修正: 遅延シーケンスをチャンク解除する簡単な方法:
(defn unchunk [s]
(when (seq s)
(lazy-seq
(cons (first s)
(unchunk (next s))))))
最初のバージョンは省略されました (when ...
そのため、入力シーケンスが終了した後、nil の無限シーケンスを返しました。
2番目のバージョンが使用されました first
の代わりに seq
それでそれはゼロで止まりました。
RE: あなたの他の質問、 「適用時にチャンク化を回避するにはどうすればよいですか。これは 4 つのチャンク化されたグループで消費されているようです。」:
これは次の定義によるものです =
, 、一連の引数が与えられると、最初の 4 つが強制されます。
(defn =
;; ... other arities ...
([x y & more]
(if (= x y)
(if (next more)
(recur y (first more) (next more))
(= y (first more)))
false)))
他のヒント
apply
が無限シーケンスで使用されるとき、それは4のグループにチャンクされた理由として、それが明らかになりますを適用する定義でclojure.coreで探しています。私は自分のソリューションを書い左てるのでReduce
は...ない短絡を行い、次のいずれか
(defn reducep
"Like reduce, but for use with a predicate. Short-circuits on first false."
([p coll]
(if-let [s (seq coll)]
(reducep p (first s) (next s))
(p)))
([p val coll]
(if-let [s (seq coll)]
(if-let [v (p val (first s))]
(recur p (first s) (next s))
false)
true)))
次に、(余分and
付き)スチュアートのunchunkを使用して
(defn unchunk [s]
(lazy-seq
(cons (first s)
(and (next s)
(unchunk (next s))))))
I入手ます:
(reducep = (map #(do (print %) %) (unchunk (range)))) ;; Prints 01, returns false
(reducep = (map #(do (print %) %) (repeat 20 1))) ;; returns true
(reducep = (map #(do (print %) %) (unchunk [0 0 2 4 5]))) ;; Returns false
(reducep = (map #(do (print %) %) (unchunk [2 2 2 2 2]))) ;; returns true
はあまりにもあなたのためにその作品の場合は、これまでの国防省。
編集:彼の編集はおそらく、この以前の記事では1に好適である後unchunkのスチュアートの修正版。
私はこの記事を見つけました そして私は、32チャンクを回避するための別の方法を見つけます:
;; add another dummy sequence parameter to the map:
(apply = (map #(do (prn %2) %) (range) (range)))
マップの高いアリティフォームはチャンクシーケンス(Clojureは1.5)を使用して表示されません。
あなたは二番目のパラメータで何かをする必要があり、そうであること そのことについて、明示的には良いかもしれない。
(apply = (map (fn [i _] (prn i) i) (range) (range)))
これは、他の解決策として、きちんとしたとしてではなく、間に合わせのために良いかもしれません 用途、テストのような「理由チャンキングのこの遅いですか」。
apply
について、あなたはシーケンスからペアを取得するためにpartition
を使用し、それらを=可能性があります:
(every? #(apply = %) (partition 2 1
(map (fn [i _] (prn i) i) (range) (range))))
reducep
があまりにも便利に見えますが。
PS。私はチャンクシーケンスが遅いという印象を与えたくない、そうではありません。 私の問題は、4clojureテストケースは私の配列生成機能以上の「最初の」と呼ぶということです 値の範囲は、私は32回の作業を行う手段をチャンク。
(PPSが。私のコードは、まだ遅すぎます)