-
-
Save plaster/4245866 to your computer and use it in GitHub Desktop.
| (use 'clojure.set) | |
| (defn n-multiples [n end] (set (range 0 end n))) | |
| (defn solve [] | |
| (apply + (union (n-multiples 3 1000) | |
| (n-multiples 5 1000)))) |
distinct! シーケンスだけでいいんですね!すごいシンプルです。
setに渡してしまうとどうやらぜんぶ評価されてしまう感じですが、distinctはどうやらちゃんと遅延してる?
core.clj中の定義を覗いてみてるのですが、lazy-seqがまだよくわかっていません。
https://github.com/clojure/clojure/blob/372f03e2fa63ff7c3544be82d85e8943e85e640b/src/clj/clojure/core.clj#L4514
あと、コメントありがとうございました。初版はキーワード引数使ってみたかったりとか、余分なのがいろいろありますね。
distinct も大変便利ですが, 集合も直感的で良いと思います.
あ, apply は, 集合に対しても適用できますので seq は無くて大丈夫です.
おお、本当にいらないですねseq。修正しました。ありがとうございます。
そういえば、applyとreduceの件で気になってることがあったのでした。私の感覚だと、
apply => 要素ぜんぶ評価して渡す
reduce => 都度シーケンスから取り出して畳み込む
だったので、どっちも使える状況ならreduceのほうがいいのでは?と思っていたのです。
でも、どうも試してみた感じ、applyのときにもちゃんと遅延してるようにみえます。
(defn my-* [& ps]
(loop [ ps ps
p 1
]
(if (nil? ps)
p
(let [[x & ps] ps]
(if (zero? x)
0
(recur ps (* p x)))))))hoge.core=> (apply my-* (concat (repeat 100000 1) [0] (repeat 1)))
0
だとすると、最終的には必ず全部シーケンスから取り出してしまうのが確定なreduceよりも、
関数の中で引数使うかどうか選べる apply のほうがいいのかもしれないですね。
数値のシーケンスの総和に関しては
(reduce + '(1 2 3 4 5)) ; -> (+ (+ (+ (+ 1 2) 3) 4) 5)
(apply + '(1 2 3 4 5)) ; -> (+ 1 2 3 4 5)なので, clojure の + がもし二引数関数なら reduce 一択なんですが, そもそも + 自体が可変長引数を受け入れる力があるので, 可変長の要素に対する対応を reduce で強制するのではなく, + が持っている機能 (たとえそれが内部的に reduce と等価だとしても) を使って上げる方が, より高い方の抽象化を使っていることになっていいんじゃないか. というのが私の考えです.
ま, おっしゃっているように, で結局 + もしくは apply の動作は自分の好み(遅延性があるかとか)にあっているか? というのも一つの尺度ですよね. 参考になります.
@kohyama のコメントを見る前だったので,なんだか同じようなことも書いていますが,私は (apply + coll) 派ですね. > http://tnoda-clojure.tumblr.com/post/37700304493/apply-and-reduce (@ponkore 版解答のコメントに貼ったのと同じ URL)
@plaster https://gist.github.com/4245866#gistcomment-621381 の apply/my-* 実験分かりやすくて参考になりました.apply に渡す関数の作り方の勉強にもなりました.
revision d23e70 を見ています.
初版から大分すっきりしましたね :-)
「割らない」というのはかっこいいですね. 思いつきませんでした.
すばらしい別解だと思います.
集合を使わない書き方を参考まで.