2

私は Clojure の初心者であり、Closure で実行できるかどうかさえわからない問題に遭遇しました。

そこで本題は以下。間隔から (制限まで) 素数を計算する関数を実装しました。

(defn gather_primes_in_range [range_start range_end target_number prime_list]
    (if (or (= 0 target_number) (> range_start range_end) (= FIND_MORE_PRIMES false)) 
        prime_list
        (do
            (if (is_prime? range_start)
                (gather_primes_in_range (+ range_start 1) range_end (- target_number 1) (conj, prime_list, range_start))
                (gather_primes_in_range (+ range_start 1) range_end target_number prime_list)
            )
        )
    )
)

(defn find_nr_of_primes_in_range [range_start range_end target_number]
    (if (< range_start 2)
        (gather_primes_in_range 2 range_end target_number [])
        (gather_primes_in_range range_start range_end target_number [])
    )
)

これはうまくいきます。しかし、私が今欲しいのは、後で検索する変数で見つかった素数を呼び出す各メソッドに格納する必要があるグローバル変数を持つことです。Python、Ruby、Scala などの他の言語では、関数から戻る前にエントリを追加する Set を使用してこれを行います。しかし、Clojure では、これを回避する方法がわかりません。

基本的に私が試したのは、どこかでグローバルに宣言したことです:

(def PRIMES_FOUND_SO_FAR #{})

そして、どういうわけか、リターン時にこの変数にエントリを追加します。これはClojureでまったく可能ですか?swap!atom、または set のいずれかを使用して値を変更するために、他の変数を試しました。しかし、どのような状況でもここで機能することはできませんでした。

4

2 に答える 2

8

まず、Clojure のコード規則について読むことを強くお勧めします。Clojureの命名規則とは何ですか?

コードの改善点をいくつかお見せしましょう。

1) clojure 命名規則の適用。

次に、 から(+ variable 1)に切り替えます(inc variable)( と同じ最適化dec)。

また(= FIND_MORE_PRIMES false)、単純に置き換えることもできますfind-more-primes?

そして最後に、条件(= 0 smthng)はより慣用的なスタイルで書くことができます(zero? smthng)

これで、コードが少し読みやすくなりました。

(defn gather-primes-in-range [range-start range-end target-number prime-list]
  (if (or (zero? target-number) (> range-start range-end) need-more-primes?)
    prime-list
    (do
      (if (is-prime? range-start)
        (gather-primes-in-range (inc range-start) range-end (dec target-number) (conj prime-list range-start))
        (gather-primes-in-range (inc range-start) range-end target-number prime-list)))))

2) ここで、冗長なdo呼び出しを削除する必要があります。これは、1 つの関数呼び出しのみをラップするためです。

そして最後のトリックは、への呼び出し全体をスワップすることにより、末尾再帰 ( http://clojure.org/special_forms#Special%20Forms--(recur%20exprs *))を適用することです。gather-primes-in-rangerecur

(defn gather-primes-in-range 
  [range-start range-end target-number prime-list]
    (if (or (zero? target-number) (> range-start range-end) need-more-primes?)
      prime-list
      (if (is-prime? range-start)
        (recur (inc range-start) range-end (dec target-number) (conj prime-list range-start))
        (recur (inc range-start) range-end target-number prime-list))))

そして、ここであなたの質問に答える時が来ました。このアプローチのメリットはありません

(def PRIMES_FOUND_SO_FAR #{})

このセットを変更する機会がないためです。対処できる唯一のことは、その構造から新しい不変データ構造を作成することです。

@georgek が言及しているように、この特定のケースでは単にatomを使用できます。

(def PRIMES_FOUND_SO_FAR (atom #{}))

アトムに新しい素数を追加する:

(swap! PRIMES_FOUND_SO_FAR conj prime-number)

値を抽出するための Deref アトム:

@PRIMES_FOUND_SO_FAR ;; or (deref PRIMES_FOUND_SO_FAR)
-> #{2 3 5 7 11}

とにかく、コードは少し必須ですが、clojure は不変のデータ構造、引数としての関数などを持つ関数型言語であることを常に覚えておく必要があります。グローバル変数を使用することはまったくお勧めできません。ところで、それはあなたの関数がclojureスタイルでどのように見えるべきかです:

(defn gather-primes-in-range [start end target-number]
  (take target-number (filter is-prime? (range start end))))
于 2013-02-10T00:24:52.397 に答える