15

私は、遅延して減らしてテストしたい、長くて怠惰なシーケンスを持っています。2 つの連続する要素=(または他の述語) が互いに一致しなくなったらすぐに、作成にコストがかかるリストの消費を停止したいと考えています。はい、これは のように聞こえますがtake-while、さらに読んでください。

私は次のようにシンプルでエレガントなものを書きたいと思っていました (のようにevery?動作するふりをしますreduce):

(every? = (range 100000000))

しかし、それは怠惰に機能しないため、無限の seq にハングアップします。これがほぼ希望どおりに機能することを発見しました。

(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が、それはかなり面倒です。

Rich Hickey に、 と を適切に組み合わせreduceevery?短絡するよう丁寧に提案する必要がありますか? それとも、既に存在する明らかな方法を見逃しているのでしょうか?

編集:apply 2 人の親切な人が、遅延シーケンスでのチャンクを回避するためのソリューションを投稿しましたが、4 つのチャンク グループで消費しているように見える を実行するときにチャンクを回避するにはどうすればよいですか?

編集 #2: Stuart Sierra が指摘し、私が独自に発見したように、これは実際にはチャンクではありません。ふつうに演技をするだけなので、これをクローズと呼び、彼に答えを出します。興味のある人のために、問題のreduce'ing部分を行うために、別の回答に小さな関数を含めました。

4

3 に答える 3

22

2回訂正:遅延シーケンスをアンチャンクする簡単な方法:

(defn unchunk [s]
  (when (seq s)
    (lazy-seq
      (cons (first s)
            (unchunk (next s))))))

最初のバージョンは省略され(when ...ていたため、入力シーケンスが終了した後に nil の無限のシーケンスが返されました。

first代わりに2 番目のバージョンが使用されseqたため、nil で停止しました。

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)))
于 2010-08-04T20:30:11.837 に答える
2

4clojure の問題で制限時間に達しているときにこの投稿を見つけ、32 個のチャンクを回避する別の方法を見つけました。

;; add another dummy sequence parameter to the map:
(apply = (map #(do (prn %2) %) (range) (range)))

アリティの高い map は、チャンクされたシーケンスを使用していないようです (clojure 1.5)

2 番目のパラメーターを使用して何かを行う必要があるため、それについて明示する方がよい場合があります。

(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 テスト ケースが値の範囲にわたって seq 生成関数で "first" を呼び出すことです。そのため、チャンキングは 32 回の作業を行うことを意味します。(PPS。私のコードはまだ遅すぎる)

于 2013-05-15T13:59:11.920 に答える
2

clojure.core で apply の定義を見ると、なぜ がapply無限シーケンスで使用されたときに 4 つのグループにまとめられていたのかが明らかになります。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)))

次に、Stuart の unchunk を使用します (追加のand)

(defn unchunk [s]
  (lazy-seq
   (cons (first s)
         (and (next s)
              (unchunk (next s))))))

私は得る:

(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

それがあなたにも当てはまる場合は、これを改造してください。

EDIT : Stuart の編集後の unchunk の修正版は、おそらくこの以前の投稿のものよりも望ましいものです。

于 2010-08-05T12:16:53.230 に答える