2012-11-08 9 views
6

"Clojure Programming"(98ページ)でヘッドの保持についての段落を読むと、split-withの例で何が起こるかわかりませんでした。私はreplで実験しようとしましたが、それは私をもっと混乱させました。Clojureのヘッド保持

(time (let [r (range 1e7) 
      a (take-while #(< % 12) r) 
      b (drop-while #(< % 12) r)] 
     [(count a) (count b)])) 
"Elapsed time: 1910.401711 msecs" 
[12 9999988] 

(time (let [r (range 1e7) 
      a (take-while #(< % 12) r) 
      b (drop-while #(< % 12) r)] 
     [(count b) (count a)])) 
"Elapsed time: 3580.473787 msecs" 
[9999988 12] 

(time (let [r (range 1e7) 
      a (take-while #(< % 12) r) 
      b (drop-while #(< % 12) r)] 
     [(count b)])) 
"Elapsed time: 3516.70982 msecs" 
[9999988] 

私はaを計算していない場合は、最後の例からもわかるように、時間がかかるが何とか成長します。私はここに何かが恋しいと思っていますが、何ですか?

+1

この質問は、良い答えを与えるhttp://stackoverflow.com/questions/15994316/clojure-head-retentionの複製です。 – robvir

答えて

1

CountはO(1)である。だからあなたの測定値はそれに依存しないのです。

+1

リスト数は実際にはO(1)ですが、 "Clojure Programming"によると、seqは実際にはリストではなく、seqの長さを取得するとコストがかかります。 –

+0

申し訳ありません。私はあなたを欺いた。はい、あなたのケースではLazySeqであり、 "count"はO(n)です。私のマシン(カウントa)で0.019ミリ秒かかる。あなたの測定のために、それはまだO(1)のように見えます。 – mobyte

+0

私の場合、それは(カウントb)ではなく、(カウントa)であるので、(カウントb)を調べてみてください。 –

0

letでバインドしてもこの値は使用されません。

(let [x (println "Side effect")] 1) 

印刷物上のコード「副作用」、およびすべてのあなたの三つの例では1

を返すには、LETの形で同じバインディングを使用したので、私はここにどんな違いが表示されません。ちなみに、私のマシンでは、すべてのスニペットはほぼ同じ時間がかかりました。

本当の違いは、あなたがこのような何かをしようとすると:

(time (let [r (range 2e7) 
     a (take 100 r) 
     b (drop 100 r)] 
    [(count a)])) 
"Elapsed time: 0.128304 msecs" 
[100] 

(time (let [r (range 2e7) 
     a (take 100 r) 
     b (drop 100 r)] 
    [(count b)])) 
"Elapsed time: 3807.591342 msecs" 
[19999900] 

によりbaは怠惰なシーケンス、O(n)時間でcount作品であるという事実に。しかし、最初の例では、bのカウントを計算しないので、ほぼ直ちに動作します。

+2

元の例では、 'r'' a'と' b'はlet文で直ちに評価されていない。これらの名前をバインドしても、シーケンスは評価されません。 –

+0

@ArthurUlfeldt correct、aおよびbは遅延セグメントにバインドされています – mishadoff

1

countは、ベクトルとリストを含むCountedコレクションのO(1)です。

一方、配列はnot countedであり、countはO(n)になります。ここで重要な部分は、関数take-whiledrop-whileがシーケンスを返すことです。彼らが怠け者であるという事実は、ここで大きな要因ではありません。ホットスポットコンパイラがまだ完全に生成されたクラスをoptomizedていないため、時間のベンチマークを使用している場合

1

、正確な結果

user> (defn example2 [] (let [r (range 1e7)            
       a (take-while #(< % 12) r)          
       b (drop-while #(< % 12) r)]       
      [(count a) (count b)])) 
#'user/example2 

user> (dorun (take 1000 (repeatedly example2))) 
nil 

user> (time (example2)) 
"Elapsed time: 614.4 msecs" 
[12 9999988] 

を取得するために、テストを何度も実行して、私は、実行時の分散を非難します。私は、第1、第2の例を数回走った混合相対的な結果だ:二回

実行例1:

autotestbed.core> (time (let [r (range 1e7)                 
             a (take-while #(< % 12) r)          
                b (drop-while #(< % 12) r)]       
           [(count a) (count b)])) 
"Elapsed time: 929.681423 msecs"                   
[12 9999988] 
autotestbed.core> (time (let [r (range 1e7)                 
             a (take-while #(< % 12) r)          
                b (drop-while #(< % 12) r)]       
           [(count a) (count b)])) 
"Elapsed time: 887.81269 msecs"                    
[12 9999988] 

はその後、例2を実行して数回:

core> (time (let [r (range 1e7)                 
        a (take-while #(< % 12) r)          
        b (drop-while #(< % 12) r)]       
      [(count a) (count b)])) 
"Elapsed time: 3838.751561 msecs"                   
[12 9999988] 
core> (time (let [r (range 1e7)                 
        a (take-while #(< % 12) r)          
        b (drop-while #(< % 12) r)]       
      [(count a) (count b)])) 
"Elapsed time: 970.397078 msecs"                   
[12 9999988] 

が第二の例をsometiemsをちょうど同じくらい速いです

0

表示されている時間は完全にシステムに依存します.... 再実行すると、いくつかの違いが表示されます各実行の経過時間を確認する