0

言語を学ぶ手段として、プロジェクトのオイラー問題を clojure で解決し始めました。問題 10 にエラトステネスのふるいを実装したとき、最初は非常に低いパフォーマンスを経験しましたboolean-array。ヘルパー関数がboolean-array親スコープから直接アクセスできるように、型ヒントを追加するか、コードを並べ替えることで問題を回避できることがわかりましたが、なぜそれが必要なのかわかりません。(type sieve)ヘルパー関数のいずれかで[Zreturn であるため、clojure はそれが . であることを既に認識しているようboolean-arrayです。ここでタイプヒントが必要な理由を誰か説明してもらえますか?

コードの壁についてお詫び申し上げます。問題を説明しながら、どの部分を削除できるかわかりません。

;;; returns a vector containing all primes smaller than limit
(defn gen-primes-orig [limit]
  (defn next-unmarked [sieve v]
    (loop [i (inc v)]
      (cond
       (>= i limit) nil
       (false? (aget sieve i)) i
       :true (recur (inc i)))))

  (defn mark-powers-of! [sieve v]
    (loop [i (+ v v)]
      (if (>= i limit) sieve
          (do
            (aset-boolean sieve i true)
            (recur (+ i v))))))

  (defn collect-primes [sieve]
    (loop [primes []
           i 0]
      (cond
       (>= i limit) primes
       (false? (aget sieve i)) (recur (conj primes i) (inc i))
       :true (recur primes (inc i)))))

  (let [sieve (boolean-array limit false)]
    ;; 0 and 1 are not primes
    (aset-boolean sieve 0 true)
    (aset-boolean sieve 1 true)

    (loop [v 0]
      (let [v (next-unmarked sieve v)]
        (if (nil? v) (collect-primes sieve)
            (do
              (mark-powers-of! sieve v)
              (recur v)))))))

(defn gen-primes-hint [limit]
  (defn next-unmarked [^booleans sieve v]
    ;; same body as in gen-primes-orig
    )
  (defn mark-powers-of! [^booleans sieve v]
    ;; same body as in gen-primes-orig
    )
  (defn collect-primes [^booleans sieve]
    ;; same body as in gen-primes-orig
    )
  (let [sieve (boolean-array limit false)]
    ;; same body as in gen-primes-orig
    ))

(defn gen-primes-letfn [limit]
  (let [sieve (boolean-array limit false)]
    ;; 0 and 1 are not primes
    (aset-boolean sieve 0 true)
    (aset-boolean sieve 1 true)

    (letfn [(next-unmarked [v]
              ;; same body as in gen-primes-orig
              )
            (mark-powers-of! [v]
              ;; same body as in gen-primes-orig
              )
            (collect-primes []
              ;; same body as in gen-primes-orig
              )]
      (loop [v 0]
        (let [v (next-unmarked v)]
          (if (nil? v) (collect-primes)
              (do
                (mark-powers-of! v)
                (recur v))))))))

3 つのブレンドのそれぞれの実行時間は次のとおりです。(結果は削除されました。すべてのブレンドで同じ正しい結果が得られます。)

user> (time (apply + (gen-primes-orig  2000000)))
"Elapsed time: 108001.047327 msecs"
user> (time (apply + (gen-primes-hint  2000000)))
"Elapsed time: 2599.091978 msecs"
user> (time (apply + (gen-primes-letfn 2000000)))
"Elapsed time: 2768.060355 msecs"
4

1 に答える 1