2009-10-30 17 views
26

Clojure:ベクトルで各要素の関数を呼び出す

そして、私はどこかにapiを持つテーブルを作成したい:

(defn setCell 
    [row col value] 
    (some code here)) 

次に、以下の呼び出しを行う最良の方法は何ですか:

(setCell 0 0 "Hello") 
(setCell 0 1 "World") 
(setCell 0 2 "Test") 
(setCell 0 3 "This") 

次のようなことがわかりました:

(let [idv (map vector (iterate inc 0) data)] 
    (doseq [[index value] idv] (setCell 0 index value))) 

しかし、新しい一時データ構造idvを必要としない高速な方法がありますか?

答えて

10

あなたのやり方は、慣用的です(実際はclojure.contrib.seq-utils/indexedと同じです)。あなたが本当に余分なデータ構造を避けたい場合は、この操作を行うことができます。

(loop [data data, index 0] 
    (when (seq data) 
    (setCell 0 index (first data)) 
    (recur (rest data) (inc index)))) 

正当な理由がないにかかわらずあった場合を除き、私はあなたのバージョンを使用すると思います。

+5

Clojure 1.2は、私がこれまで見てきた最もシンプルで最適なオプションである 'map-indexed'を追加しました。 [Rolloの答えは以下を参照](http://stackoverflow.com/a/5992602/109618)。 –

27

インデックスとデータをマッピングするだけで、非常に密接に似たような効果を得ることができます。

(map #(setCell 0 %1 %2) (iterate inc 0) data) 

あなたは通話が今実現するために(doallまたは(doseqでこれをラップすることをお勧めします。最短のseqがなくなったときに地図が停止するので、有限のseqと有限のseqをマップするのはいいです。

+1

ニース、私は複数のコレクションに適用された場合、マップのこの動作を知らなかった。 – pmf

+10

3年間のフォローアップ:(iterate inc 0)はちょうど(範囲)で書かれた方が良い –

1

私はSOFARオプションのパフォーマンスの短い比較をした:私の1つのコアラップトップ上

; just some function that sums stuff 
(defn testThis 
    [i value] 
(def total (+ total i value))) 

; our test dataset. Make it non-lazy with doall  
(def testD (doall (range 100000))) 

; time using Arthur's suggestion 
(def total 0.0) 
(time (doall (map #(testThis %1 %2) (iterate inc 0) testD))) 
(println "Total: " total) 

; time using Brian's recursive version 
(def total 0.0) 
(time (loop [d testD i 0] 
    (when (seq d) 
    (testThis i (first d)) 
    (recur (rest d) (inc i))))) 
(println "Total: " total) 

; with the idiomatic indexed version 
(def total 0.0) 
(time (let [idv (map vector (iterate inc 0) testD)] 
    (doseq [[i value] idv] (testThis i value)))) 
(println "Total: " total) 

結果:

"Elapsed time: 598.224635 msecs" 
    Total: 9.9999E9 
    "Elapsed time: 241.573161 msecs" 
    Total: 9.9999E9 
    "Elapsed time: 959.050662 msecs" 
    Total: 9.9999E9 

予備的結論:

をループ/ RECURを使用します溶液。

+0

リッチは、各テストを数回実行し、最後にホットスポットオプティマイザをウォームアップするためにマイクロベンチマーキングを提案しました。 testThisはmap関数より軽いので、できるだけタイトなループが良いでしょう。 –

+0

私はホットスポット最適化でテストする必要があると思います。 私はちょうど同じテストをPythonで実行しました。そこでは80ミリ秒で実行されました。 –

+0

そのような 'def'を呼び出すと、あなたのしおりを揺さぶっているかもしれません。 –

8

素敵な方法は、(非構造を使用して)次のようになりますこれ、clojure.contrib.seq-utils/indexedを使用することです:

(doseq [[idx val] (indexed ["Hello" "World" "Test" "This"])] 
    (setCell 0 idx val)) 
+1

[clojure-contribは現在非推奨](http://clojure.github.io/clojure-contrib/seq-utils-api.html)として、 'indexed'関数はどこに行きましたか? [Clojure.Contribはどこに行ったのですか(http://dev.clojure.org/display/community/Where+Did+Clojure.Contrib+Go)文書はそれについて何も言わないようです。 – Alexey

+1

私はちょうど1つを書くことになった。 (idx val)(索引付きcoll)(..)) 'がmap-indexedよりも明確なコードを生成することがあるからです。 '(defn indexed [coll](map-indexed vector coll)) ' – prabhasp

27
ビットゲーム後半で

が、このページにアクセスする人のために:Clojureの1.2以降(今そこにあります)map-indexed関数はclojure.coreで利用可能です。

1つの問題(私が間違っている場合を除きます):マップインデックスの計算を簡単に並列化できないという意味の「pmap」はありません。その場合、上記のソリューションを参照したいと思います。

+1

'pmap-indexed'は、インクリメントするイテレータをpmapの最初のコレクションとしてインクルードすることで実行できます。'(defn pmap-indexed [f coll](pmap f(iterate inc 0)coll)) '。 – tkocmathla

関連する問題