1:いいえrecur
かなり速いです。呼び出す関数ごとに、VM からのオーバーヘッドと「ノイズ」が少しあります。たとえば、REPL は呼び出しを解析して評価する必要があります。そうしないと、ガベージ コレクションが発生する可能性があります。そのため、このような小さなコードのベンチマークは何の意味もありません。
と比べて:
(defn
count-leading-hash
[line]
(let [cnt (count (take-while #(= % \#) line))]
(if (> cnt 6) 6 cnt)))
(defn
count-leading-hash2
[line]
(loop [cnt 0]
(if (and (= (.charAt line cnt) \#) (< cnt 6))
(recur (inc cnt))
cnt)))
(def lines ["### Line one" "######## Line two"])
(time (dorun (repeatedly 10000 #(dorun (map count-leading-hash lines)))))
;; "Elapsed time: 620.628 msecs"
;; => nil
(time (dorun (repeatedly 10000 #(dorun (map count-leading-hash2 lines)))))
;; "Elapsed time: 592.721 msecs"
;; => nil
大きな違いはありません。
2:この場合、 loop
/recur
の使用は慣用的ではありません。本当に必要な場合にのみ使用し、可能な場合は他の利用可能な機能を使用することをお勧めします。コレクション/シーケンスを操作する便利な関数が多数あります。参照と例については、ClojureDocsを確認してください。私の経験では、関数型プログラミングに不慣れな命令型プログラミングのスキルを持つ人は、Clojure の経験が豊富な人よりもはるかに多く使用しloop
ます。/コードのにおいがすることがあります。recur
loop
recur
3: 最初のバージョンの方が好きです。さまざまなアプローチがあります。
;; more expensive, because it iterates n times, where n is the number of #'s
(defn count-leading-hash [line]
(min 6 (count (take-while #(= \# %) line))))
;; takes only at most 6 characters from line, so less expensive
(defn count-leading-hash [line]
(count (take-while #(= \# %) (take 6 line))))
;; instead of an anonymous function, you can use `partial`
(defn count-leading-hash [line]
(count (take-while (partial = \#) (take 6 line))))
編集:partial
匿名関数と比較して、
いつ使用するかを決定する方法は?
(partial = \#)
は と評価されるため、パフォーマンスに関しては問題ありません(fn [& args] (apply = \# args))
。#(= \# %)
に変換され(fn [arg] (= \# arg))
ます。どちらも非常に似ていpartial
ますが、任意の数の引数を受け入れる関数を提供するため、それが必要な状況では、それが適しています。ラムダ計算partial
の λ (ラムダ)です。読みやすいものを使用するか、任意の数の引数を持つ関数が必要な場合に使用します。partial