Created
December 15, 2012 09:08
-
-
Save emanon001/4292285 to your computer and use it in GitHub Desktop.
Project Euler Problem 6 #mitori_clj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; Project Euler Problem 6 | |
;;; 「二乗の和と和の二乗の差はいくつか?」 | |
;;; http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%206 | |
(use 'clojure.test) | |
(defn sum-of-squares | |
"与えられた数列についてその二乗の和を返します" | |
[nums] | |
(apply + (map #(* % %) nums))) | |
(defn square-of-sum | |
"与えられた数列についてその和の二乗を返します" | |
[nums] | |
(apply * (repeat 2 (apply + nums)))) | |
(defn solve | |
"最初の n 個の自然数について、二乗の和と和の二乗の差を返します" | |
[n] | |
(->> (range 1 (inc n)) | |
((juxt square-of-sum sum-of-squares)) | |
(apply -))) | |
(is (= (solve 10) 2640)) | |
(time (solve 100)) |
重要な理由として DRY (Don't Repeat Yourself) 原則があります.
同じことをするコードを複数書いてしまうと, 実装を変更する時にそれら全てを更新しなければならないので良くありません.
#(* % %)
の部分や (range 1 (inc n))
がもっと大きなコードであった場合を想定し, 一度しか書かないためのイディオムを演習しておくと良い. と思います.
その上で, 抽象化しようとしているものの性質
「シーケンスの各要素の計算が高コストなのでメモリを使ってでも二度は計算したくない」のか、 「(もしかしたらものすごく長い)繰り返しの抽象化として遅延シーケンスを使っているのか」
で扱いを変えたほうが良いというのは, なるほど. 確かにおっしゃる通りですね.
今後, 意識してみようと思います.
前者については, Problem 14 の方で話題にあがっている「メモ化」も参考になるかと思います.
juxt
は面白い関数なのですが、使うのにちょうど良い場面になかなか遭遇しない(ぼくが気づいていない)ですね。
うまく使えている例だと思います。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@kohyama
私の解を読み返したところ、
(range 1 (inc n))
を見事に2度書いてしまっていました。ふと気になったのですが、同じ遅延シーケンスを2箇所で順に使おうとすると、片方で使うために計算し終えた要素すべて、もう片方で使うまでメモリに持つことになりますよね。
let
でもjuxt
でもきっとそうだと思います。とすると「シーケンスの各要素の計算が高コストなのでメモリを使ってでも二度は計算したくない」のか、 「(もしかしたらものすごく長い)繰り返しの抽象化として遅延シーケンスを使っているのか」で扱いを変えたほうがよい気がします。後者でやりたい場合は「シーケンスを生成する関数」でやればよいでしょうか。
これを @tnoda さんの例にならって関数合成で書いてみたらこんな感じでしょうか。
もうちょっとシンプルになりそうな気もするのですが……