1

私はClojureに関する多くのドキュメントを読み(そしてもう一度読む必要があります)、言語の「感触」をつかむためにSOでいくつかのClojureの質問を読みました。elisp のいくつかの小さな関数を除けば、これまで Lisp 言語で書いたことはありません。Clojure で最初のプロジェクト Euler ソリューションを作成しましたが、先に進む前にmapreduceについて理解を深めたいと思います。

ラムダを使用して、次のようになりました(3または5の倍数、または1から1000までの両方の倍数をすべて合計するため):

(reduce + (map #(if (or (= 0 (mod %1 3)) (= 0 (mod %1 5))) %1 0) (range 1 1000)))

REPLに書いたので、1行にまとめました(そして、正しい解決策が得られます)。

ラムダなしで、私はこれを書きました:

(defn val [x] (if (or (= 0 (mod x 3)) (= 0 (mod x 5))) x 0))

そして、これを行う解を計算します。

(reduce + (map val (range 1 1000)))

どちらの場合も、 reduceを実行する前に map が何を返すべきかに関する質問です。マップを実行した後、次のようなリストになっていることに気付きました: (0 0 3 0 5 6 ...)

val定義の末尾にある「0」を削除しようとしましたが、 (nil nil 3 nil 5 6 etc.)で構成されたリストを受け取りました。nilが問題かどうかはわかりません。ゼロが実際に問題にならないように、とにかく左折をしながら合計しようとしていることがわかりました。

それでもなお、返すべき賢明なマップとは何でしょうか? (0 0 3 0 5 6 ...) または (nil nil 3 nil 5 6 ...) または (3 5 6 ...) (この最後のものについてどうすればよいでしょうか?) または何か他のもの?

ゼロ/ゼロを「除外」する必要がありますか?

私は基本的な質問をしていることを知っていますが、マップ/リデュースは明らかに私がよく使うものなので、どんな助けも大歓迎です.

4

3 に答える 3

2

マッピングの懸念を削減から分離する必要性をすでに直感的に理解しているようです。削減によって使用されないマップによって生成されたデータを持つことは完全に自然です。実際、ゼロが加算の恒等値であるという事実を使用すると、これがさらにエレガントになります。

  • mappings ジョブは新しいデータを生成することです (この場合は 3 5 または「無視」)
  • reduce の仕事は、何を含めるかを決定し、最終結果を生成することです。

あなたが始めたのは慣用的な clojure であり、これ以上複雑にする必要はないので、次の例は map に何を含めるかを決定させるポイントを説明するためのものです:

(reduce #(if-not (zero? %1) (+ %1 %2) %2) (map val (range 10)))

この不自然な例では、reduce 関数はゼロを無視します。典型的な実世界のコードでは、アイデアがいくつかの値をフィルタリングするのと同じくらい単純である場合、人々は単にfilter関数を使用する傾向があります

(reduce + (filter #(not (zero? %)) (map val (range 10))))

フィルターから始めて、マップをスキップすることもできます:

(reduce + (filter #(or (zero? (rem % 3)) (zero? (rem % 5))) (range 10)))
于 2012-04-27T20:34:59.967 に答える
1

合言葉は明快さです。

  • filterではなく、使用してくださいmap。そうすれば、後で行動しないと決定しなければならない null 値を選択する必要がなくなります。
  • フィルタリング/マッピング関数に名前を付けると役立ちます。他の場所で関数を使用する場合を除き、 ではなくlet orを使用してください。letfndefn

このアドバイスに従って行動すると、次のことがわかります...

(let [divides-by-3-or-5? (fn [n] (or (zero? (mod n 3)) (zero? (mod n 5))))]
  (reduce + (filter divides-by-3-or-5? (range 1 1000))))

ここで一旦停止することもできます。

これはよく読めますが、divides-by-3-or-5?機能が喉に刺さります。要素を変更すると、まったく新しい関数が必要になります。そして、その繰り返されるフレーズ(zero? (mod n ...))の瓶。そう ...

考えられる要素のリスト (またはその他のコレクション) が与えられた場合に、それらのいずれかが特定の数値に当てはまるかどうかを示す関数が必要です。言い換えれば、私たちが望むのは

  • 数の集合の関数 - 考えられる要因 - ...
  • 1つの数値の関数を返す- 候補 - ...
  • これは、候補が可能な要因のいずれかで割り切れるかどうかを示します。

そのような関数の 1 つが

(fn [ns] (fn [n] (some (fn [x] (zero? (mod n x))) ns)))

こうやって使える・・・

(let [divides-by-any? (fn [ns] (fn [n] (some (fn [x] (zero? (mod n x))) ns)))]
  (reduce + (filter (divides-by-any? [3 5]) (range 1 1000))))

ノート

  • この「改善」により、プログラムが少し遅くなりました。
  • divides-by-any?に昇格するのに十分役立つかもしれません defn
  • 操作が重要な場合は、冗長な要素を取り除くことを検討できます。たとえば、[2 3 6]に減らすことができます[6]
  • 操作が非常に重要であり、係数が定数として提供されている場合は、 の使用に戻るマクロを使用してフィルター関数を作成することを検討できますor

これは少し毛むくじゃらの話ですが、あなたが言及している問題によって引き起こされた考えを詳しく述べています.

于 2014-04-06T11:32:26.933 に答える
0

あなたの場合、私はの代わりにkeepmapを使用します。mapnil以外の値のみを保持することを除いて、と同様です。

于 2012-04-28T07:55:59.440 に答える