Last active
February 18, 2017 06:52
-
-
Save plaster/4357479 to your computer and use it in GitHub Desktop.
Project Euler Problem 16 solution in Clojure
This file contains 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 16 solution | |
;;;; http://projecteuler.net/problem=16 | |
(use 'clojure.test) | |
(import 'java.math.BigInteger) | |
;; Character/digit usage from: https://gist.github.com/4276901 | |
(def solve | |
(comp (partial apply +) | |
(partial map #(Character/digit ^char % 10)) | |
str | |
#(. (BigInteger. "2") pow %) | |
;; no reflection warnings at this method call. really? | |
;; cf. #(. % pow %2) => reflection warning "call to pow can't be resolved." | |
)) | |
(is (= 26 (solve 15))) | |
(is (= 1366 (solve 1000))) |
@ypsilon-takai さんの指摘どおり、この問題なら数値のまま解くのが自然ですね。文字列化するにも内部的にはmodとquotしてるでしょうから、実行効率もよさそうな気がします。
@tnoda さんの lazy-seq
わかりやすくて素敵です。
別のところで話題になっていた気がしますが、再帰のどこに lazy-seq を入れるか悩みどころです。
ほとんど好みの問題のような気もするのですが、(cons (rem x 10) (lazy-seq (digits* (quot x 10))))
のように書きたくなります。
- 全体を
lazy-seq
にすると、先頭の要素すら、とり出されるまで計算されない
** 一方で、先頭の要素を取り出すだけで再帰呼び出しが1段進んで、(pos? x)
は評価される
VS - 再帰呼び出しだけを
lazy-seq
にすると、先頭の要素を取り出しただけでは他の計算は何も起きない
** ただし、先頭の要素の(rem x 10)
は即座に評価される
徹底するならいっそ、if
の外にまで lazy-seq
を出してもいいのかも? しれません。
真面目にトレードオフを考える必要がある場面があるとしたらどういうときなのか、もうちょっと考えてみます。
あと、 pos?
みたいな基本的な関数をちゃんと使えるようにしたいですね。覚えます。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@ypsilon-takai さん
「文字列化すれば文字のシーケンスが手に入るので...」という方法でなく, 数値のまま処理するとすれば, 御解答は模範的ですね.
言われてみればその方が普通のような気がします.
@tnoda さん
同じく lazy-seq 版, いいですね.
個人的に遅延評価と再帰で書くのは好きです.
目的は和なので
reverse
しなくても良いのですが, ヘルパー関数 digits として外に出したので, 少し汎用的に上の桁から順に返すようにしたのだと推測します.digits* の「下の桁から一桁ずつの数値のシーケンスを返す関数」という仕様を, 名前や doc-string やコメントで, 伝える工夫をして reverse はしないというのもありかもしれません.
局所性の高い名前は
defn-
やdef ^:private
を使って, 名前空間の外に export されないようにする, というのは良い習慣ですね.見習いたいです.