いわゆる卍研究所。
In: Clojure
26 2月 2010この話題は去年の間に収束したとおもっていたのですが、再燃してきているみたいなので記事にしておきます。
Clojure使いを何と呼ぶか。
結論から言うと “Clojurian“。
実はRich本人がすでに言及しています。
The A-Z of Programming Languages: Clojure (Page 3)
interviewer: Perl gurus are ‘Perl Mongers’, Python ones are ‘Pythonistas’. We think Clojure needs something similar. Any suggestions?
Rich: I think everyone has settled on Clojurians.
というわけでもうClojurianでいいよね。> all
「Clojure的な」についてはわかりません。
どなたか知っている方はおられませんか?
In: Clojure
13 2月 2010前エントリ clojure.lib コーディング規約・訳 から1週間以上がすぎました。
Google Groupsでのディスカッションで合意されたコーディング規約をStuがまとめてアップしてくれました。
Clojure Library Coding Standards | Clojure | Assembla
さっそく和訳してみました。
間違いがあればご指摘ねがいます。 ⇒ @manjilab
【和訳ここから】
免責事項:
規約:
【和訳ここまで】
In: Clojure
4 2月 20102/13 – 更新された規約はこちらの記事にまとめました。
⇒
“Clojureライブラリ・コーディング規約” まとめ
Clojure Dev Google groupsで盛り上がっているトピックがあります。
“clojure.lib coding standards: initial draft brain dump Options”
Programming Clojure の著者、Stuart Halloway氏が音頭を取ってclojure.lib用のコーディング規約をみんなで決めようというものです。
興味深かったのでStuの提案したルールを和訳してみました。
思いっきり意訳です。間違いがあればご指摘ねがいます。
⇒ @manjilab
(掲示板の流れに合わせていくつかの項目を追加・削除しています)
【和訳ここから】
最初に:
規約:
【和訳ここまで】
加筆
【2010.2.4 – 10:30】
・『マップ要素へのアクセスは (:property object-like-map)〜』を追加
・bang! の項:「安全でないSTMトランザクション」 ⇒ 「STMトランザクションで安全でないもの」
@athos0220さん、ご指摘ありがとうございました。
In: Clojure
30 1月 2010Clojureコードバトンが回ってきたので挑戦してみたよ。
数日前もにいちど @nitro_idiotさんから打診されてた のですが渡航中だったのでパスさせていただいてました。
帰国後に @pokarimさんが回してくださった のでありがたく参加させていただくことに。
とはいえgithubもgistも他の人のソースをforkしたことがないんだよね。なんか不安。いいのか?
forkは実際にやってみると簡単でした。gistにログインしてforkボタンを押してあとはweb上で編集するだけ。
渡航中にチェックしたときは僕にもリファクタリングできそうな箇所が2つほどあったので安心してたのですが、先ほど見てみるとキレイに修正済でした。しまった。
いきなりハードルがあがってしまいましたがすでに受けてしまったので、機能追加の方向でなんとかします。
実行環境が Mac OS X 上の場合のみ、出題時に英単語を音声で出力するようにしました。
speak-osx と osx? が追加部分です。
これを読んだ方で興味をもたれた方は是非バトンを受け取って(forkして)参加してください。おまちしております。
追記
【2010.1.31】
@deltamさんが受け取ってくれました ありがとうございま〜す。
【2010.2.1】
@omasanoriさんにバトンが無事に渡ったようです。
ほっ。
In: Project Euler
16 1月 2010Project EulerをClojureでおさらいシリーズ。
Problem 22
日本語訳:
5000個以上の名前を含む46Kのテキストファイル name.txt を用いる。 まずアルファベット順にソートすること。 そしてそれぞれの名前のについてアルファベットの数値とリスト内での順番を掛けて名前のスコアを計算する。
たとえばアルファベット順にソートされたリストの938番目に COLIN があるとすると、COLINは 3 + 15 + 12 + 9 + 14 = 53 であるので 938 × 53 = 49714 がスコアとなる。
ファイルに含まれるすべての名前のスコアの合計を求めよ。
外部ファイルからデータを読み込む最初の問題。
以前Rubyで解いたもの:
#!/usr/bin/env ruby f = open('names.txt') names_orig = f.read f.close def score_base(name) name.unpack("C*").inject(0){|s,x| s + x - 64} end names = names_orig.split(',').map{|name| name.tr('"','')}.sort p names.enum_for(:each_with_index).map{|name,i| score_base(name) * (i+1)}.inject(:+)
Clojureではこのように:
(ns euler.p022) (def names (slurp "names.txt")) (defn name-score "Return name score. ex) COLIN at 938th in list -> 938 * (3 + 15 + 12 + 9 + 14) = 49714" [pos name] (* pos (reduce + (map #(-> % int (- 64)) name)))) (println "Anster : " (reduce + (map name-score (iterate inc 1) (sort (re-seq #"\w+" names)))))
mapが使える言語だととっても楽チン。
In: Project Euler
15 1月 2010Project EulerをClojureでおさらいシリーズ。
Problem 21
日本語訳:
n の真の約数の合計を d(n) と定義する。
(真の約数とは整数 n の約数で n 以外のものである)もし d(a) = b かつ d(b) = a が a ≠ b で成立するならば a と b は 友愛数と呼ばれる。
たとえば220の真の約数は 1,2,4,5,10,11,20,22,44,55,110 で
d(220) = 284 であり、284の真の約数は 1,2,4,71,142 なので
d(284) = 220 となる。10000未満の友愛数の合計を求めよ。
以前Rubyで解いたもの:
#!/usr/bin/env ruby require 'mathn' def divisor(n) result=[1] n.prime_division.each do |i| result = result + result.map {|x| (1..i[1]).map{|n| x*(i[0]**n)}}.flatten end result.flatten.sort end def sum_of_divisors(n) divisor(n)[0..-2].inject(:+) end sum = 0 (1...10000).each do |n| sod = sum_of_divisors(n) next if sod.nil? next if n == sod sum += n if n == sum_of_divisors(sum_of_divisors(n)) end p sum
約数の和は prime_division で素因数分解したものから約数リストを生成して、最後の要素を除いたものを inject で合計してます。
さてClojureでは:
(ns euler.p021 (:use clojure.contrib.seq-utils)) (defn prime-division "Return prime factors of N." [number] (loop [n number d 2 factors []] (cond (<= n d) (frequencies (conj factors d)) (zero? (rem n d)) (recur (/ n d) d (conj factors d)) :else (recur n (inc d) factors)))) (defn exp-prime-factor "Expand prime factor exponentially. Usage: (exp-prime-factor prime-factor index) Example: (exp-prime-factor 3 4) -> [1 3 9 27 81]" [pf idx] (map #(apply * (replicate % pf)) (range (inc idx)))) (defn composite-factors "Composite factors collection. Usage: (composite-factors coll [prime-factor index]) Example: (composite-factors [1 2 4] [5 2]) -> (1 2 4 5 10 20 25 50 100)" [last-coll coll] (for [x (apply exp-prime-factor coll) y last-coll] (* x y))) (defn sum-of-proper-divisors "Return sum of proper divisor of N." [n] (apply + (drop-last (reduce composite-factors [1] (prime-division n))))) (def sum-of-proper-divisors (memoize sum-of-proper-divisors)) (defn amicable? "Return true if N has amicable pair." [n] (let [candidate (sum-of-proper-divisors n)] (if (not= candidate n) (= n (sum-of-proper-divisors candidate))))) (println "Answer : " (apply + (filter amicable? (range 10000))))
うわー、なんか長くなった。
匿名関数は1行で収まる簡単な定義のみに使う。
ということで、素因数分解から約数のリストを生成する工程を幾つかの関数に 切り分けたのが原因。
そして関数の名付けがむずかしくてドキュメントで補足するハメに・・。
まだまだ未熟よのう。
ためしに1関数にまとめてみたらこうなった。
(ns euler.p021 (:use clojure.contrib.seq-utils)) (defn prime-division "Return prime factors of N." [number] (loop [n number d 2 factors []] (cond (<= n d) (frequencies (conj factors d)) (zero? (rem n d)) (recur (/ n d) d (conj factors d)) :else (recur n (inc d) factors)))) (defn sum-of-proper-divisors "Return sum of proper divisor of N." [n] (apply + (drop-last (reduce (fn [m-coll coll] (mapcat (fn [m-factor] (map (fn [idx] (* m-factor (. (bigint (first coll)) pow idx))) (range (inc (second coll))))) m-coll)) [1] (prime-division n))))) (def sum-of-proper-divisors (memoize sum-of-proper-divisors)) (defn amicable? "Return true if N has amicable pair." [n] (let [candidate (sum-of-proper-divisors n)] (if (not= candidate n) (= n (sum-of-proper-divisors candidate))))) (println "Answer : " (apply + (filter amicable? (range 10000))))
累乗はJava側の関数にやらせてみた。 やっぱりこっちの方がいいかな。
In: Project Euler
14 1月 2010Project EulerをClojureでおさらいシリーズ。
Problem 20
日本語訳:
n! は n × (n-1) × ・・・ × 3 × 2 × 1 を表す。
1000! の各桁の数字の和を求めよ。
瞬殺の予感。
以前Rubyで解いたもの:
#!/usr/bin/env ruby p 1.upto(100).inject(:*).to_s.split('').inject(0){|s,x| s + x.to_i}
Rubyステキ。
Clojureでは:
(ns euler.p020) (defn fact "Return factorial of N." [n] (apply * (range 1 (inc n)))) (defn sum-of-digits "Return the sum of each digits of N." [n] (apply + (map #(Integer. (str %)) (str n)))) (println "Answer : " (sum-of-digits (fact 100)))
ほとんどいままでの使い回し。
In: Project Euler
13 1月 2010Project EulerをClojureでおさらいシリーズ。
Problem 19
日本語訳:
まず次の情報を与えておこう。(自分自身で確認してみるのもよいだろう)
- 1900年1月1日は月曜日
- 4月、6月、9月、11月は30日まで、
それ以外と2月を除く月は31日まである。
2月は28日までだが、閏年には29日までとなる。- 閏年は西暦の数字が4で割り切れる年におこる。
ただし400で割り切れず100で割り切れる場合は除く。20世紀(1901年1月1日〜2000年12月31日)には月の始めにくる日曜日がいくつあるか求めよ。
グレゴリオ暦を扱う問題。
以前Rubyで解いたもの:
#!/usr/bin/env ruby days_of_month = [[31,28,31,30,31,30,31,31,30,31,30,31], [31,29,31,30,31,30,31,31,30,31,30,31]] Monday = 1 Sunday = 0 def is_leap(year) year % 4 == 0 && year % 100 != 0 || year % 400 == 0 end count = 0 days_total = Monday + 365 # 1900 is not leap (1901..2000).each do |year| leap = is_leap(year) ? 1 : 0 (1..12).each do |month| count += 1 if days_total % 7 == 0 days_total += days_of_month[leap][month-1] end end p count
おもいっきり手続き型ですね。
今回のClojureでは:
(ns euler.p019) (def days-of-month [31 28 31 30 31 30 31 31 30 31 30 31]) (defn leap-year? "Return true if leap year." [year] (cond (zero? (rem year 400)) true (zero? (rem year 100)) false (zero? (rem year 4)) true :else false)) (defn leaps-from-origin "Return number of leap days form Jan-0 0000." [year month day] (+ (quot year 4) (- (quot year 100)) (quot year 400) (if (leap-year? year) (if (< month 3) -1 0) 0))) (defn days-total-from-origin "Return number of days from Jan-0 0000." [year month day] (+ (* 365 year) (apply + (take (dec month) days-of-month)) day (leaps-from-origin year month day))) (defn sunday? "Return true if date is sunday." [year month day] (let [sunday (rem (dec (days-total-from-origin 1900 1 1)) 7)] (= sunday (rem (days-total-from-origin year month day) 7)))) (println "Answer : " (count (filter #(apply sunday? %) (for [year (range 1901 2001) month (range 1 13)] [year month 1]))))
月初の日付を生成して、日曜日の判定を別関数にしました。
さてひととおり書いた後に 「Clojureにも日付を扱うライブラリくらいあるだろ」 と思い探してみましましたが見当たりませんでした。
こういうときはJavaを探すというお約束。→
ありました
グレゴリオ暦のほうじゃないと閏年の修正が異なるので注意。
これを使って書き直すと:
(ns euler.p019 (:import (java.util GregorianCalendar))) (defn sunday? "Return true if date is sunday." [year month day] (let [date (doto (GregorianCalendar.) (.set year (dec month) day))] (= (. date get GregorianCalendar/DAY_OF_WEEK) GregorianCalendar/SUNDAY))) (println "Answer : " (count (filter #(apply sunday? %) (for [year (range 1901 2001) month (range 1 13)] [year month 1]))))
かなりスッキリしました。
In: Project Euler
12 1月 2010Project EulerをClojureでおさらいシリーズ。
Problem 18
日本語訳:
下の三角形の頂点から隣接する数字を下まで辿っていくと、その数値の和の最大数は23となる。
3 7 4 2 4 6 8 5 9 3この例の場合、3 + 7 + 4 + 9 = 23 である。
以下の三角形において、同様に和の最大数を求めよ。
75 95 64 17 47 82 18 35 87 10 20 04 82 47 65 19 01 23 75 03 34 88 02 77 73 07 63 67 99 65 04 28 06 16 70 92 41 41 26 56 83 40 80 70 33 41 48 72 33 47 32 37 16 94 29 53 71 44 65 25 43 91 52 97 51 14 70 11 33 28 77 73 17 78 39 68 17 57 91 71 52 38 17 14 91 43 58 50 27 29 48 63 66 04 68 89 53 67 30 73 16 69 87 40 31 04 62 98 27 23 09 70 98 73 93 38 53 60 04 23注意:この例では16384通りのルートしかないのですべての経路を調べることもできる。しかし Problem 67 では同様の問題でも三角形が100行からなるので総当たりで解くのは不可能である。もっと賢い方法が求められる。
選択肢の枝をどのように切り落としていくかが問題。
以前Rubyで解いたもの:
#!/usr/bin/env ruby Rows = 15 tri = [] tri << %w(75) tri << %w(95 64) tri << %w(17 47 82) tri << %w(18 35 87 10) tri << %w(20 04 82 47 65) tri << %w(19 01 23 75 03 34) tri << %w(88 02 77 73 07 63 67) tri << %w(99 65 04 28 06 16 70 92) tri << %w(41 41 26 56 83 40 80 70 33) tri << %w(41 48 72 33 47 32 37 16 94 29) tri << %w(53 71 44 65 25 43 91 52 97 51 14) tri << %w(70 11 33 28 77 73 17 78 39 68 17 57) tri << %w(91 71 52 38 17 14 91 43 58 50 27 29 48) tri << %w(63 66 04 68 89 53 67 30 73 16 69 87 40 31) tri << %w(04 62 98 27 23 09 70 98 73 93 38 53 60 04 23) Tri = tri.map{|line| line.map{|num| num.to_i}} $cache = tri.map{|line| line.map{|num| nil}} def pick_grater(depth, pos) current = Tri[depth][pos] return current if depth == Rows - 1 $cache[depth+1][pos] ||= pick_grater(depth + 1, pos) $cache[depth+1][pos+1] ||= pick_grater(depth + 1, pos + 1) left = $cache[depth+1][pos] right = $cache[depth+1][pos+1] (left > right ) ? (left + current) : (right + current) end p pick_grater(0, 0)
再帰&キャッシュです。
頂点からスタートして
最大数 =(左右の選択肢が持つ和の大きいほう)+(自分の数字)
で、あとは再帰。
さて、次はClojureで書いてみます。
書籍 “Programming Clojure” より “Six Rules of Clojure FP” の5つめ:
Know the sequence library. You can often write code without using recur or the lazy APIs at all.
最近はこれを心がけています。
Ruby版のは再帰ですが実際の処理としては枝の最下部から切り落とされていきます。
だったら最下層から折りたたんでいけば再帰を使わなくてもいけそうです。
(ns euler.p018) (def triangle-text "75 95 64 17 47 82 18 35 87 10 20 04 82 47 65 19 01 23 75 03 34 88 02 77 73 07 63 67 99 65 04 28 06 16 70 92 41 41 26 56 83 40 80 70 33 41 48 72 33 47 32 37 16 94 29 53 71 44 65 25 43 91 52 97 51 14 70 11 33 28 77 73 17 78 39 68 17 57 91 71 52 38 17 14 91 43 58 50 27 29 48 63 66 04 68 89 53 67 30 73 16 69 87 40 31 04 62 98 27 23 09 70 98 73 93 38 53 60 04 23") (def triangle (for [row (map #(re-seq #"[0-9]+" %) (.split triangle-text "\n"))] (map #(Integer. %) row))) (defn merge-rows "Return maximum values for upper row." [last-row current-row] (map + current-row (map #(apply max %) (partition 2 1 last-row)))) (println "Answer : " (first (reduce merge-rows (reverse triangle))))
partition 関数の使い方がミソですね。
“Programming Clojure” の “5.3 Lazier Than Lazy” が参考になりました。
merge-row では左右の和の最大数を比較して、上の階層に渡す和を求めています。あとはreduceで繰り返し。
In: Project Euler
11 1月 2010Project EulerをClojureでおさらいシリーズ。
Problem 17
日本語訳:
1から5までを英単語で表すと one, two, three, four, five となり、 文字数の合計は 3 + 3 + 5 + 4 + 4 = 19 となる。
1から1000までを英単語で表すと文字数の合計はいくらになるか。
注意:スペースとハイフンはカウントしない。例えば 342 (three hundred and forty-two) は23文字となり、115 (one hundred and fifteen) は20文字となる。 英単語での表記の”and”の使い方はイギリス英語のそれに準じることとする。
fourteen と forty の綴りには注意。あとはそんなに難しくない。
以前Rubyで解いたもの:
#!/usr/bin/env ruby One_to_Nine = %W(#{} one two three four five six seven eight nine) Teen = %W(#{} #{} #{} thir four fif six seven eigh nine) Ty = %W(#{} #{} twen thir for fif six seven eigh nine) Ten_to_Twelve = %w(ten eleven twelve) Handred = " handred" Thousand = " thousand" def spell(num) r = [] d = [] (0..3).each do |digit| d[digit] = num % 10**(digit+1) / 10**digit end r << One_to_Nine[d[3]] + Thousand if d[3] > 0 r << One_to_Nine[d[2]] + Handred if d[2] > 0 r << "and" if (d[3] + d[2]) * (d[1] + d[0]) > 0 if d[1] == 1 r << Teen[d[0]] + "teen" if d[0] > 2 r << Ten_to_Twelve[d[0]] if d[0] <= 2 else r << Ty[d[1]] + "ty" if d[1] > 1 r << One_to_Nine[d[0]] if d[0] > 0 end r.join(" ").sub("ty ", "ty-") end def count_letter(num) spell(num).tr(" -", "").size end p 1.upto(1000).inject(0){|s,n| s + count_letter(n)}
効率化しようとしてムダに力が入っているな・・。
今回はClojureで:
(ns euler.p017) (def one-to-nineteen ["" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine" "ten" "eleven" "twelve" "thirteen" "fourteen" "fifteen" "sixteen" "seventeen" "eighteen" "nineteen"]) (def twenty-to-ninety ["twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety"]) (defn spell-num "Return number as english-word." ([n] (spell-num n "")) ([n preword] (cond (= n 0) "" (< n 20) (str preword (nth one-to-nineteen n)) (< n 100) (str preword (nth twenty-to-ninety (/ (- n 20) 10)) (spell-num (rem n 10) "-")) (< n 1000) (str preword (spell-num (/ n 100)) " hundred" (spell-num (rem n 100) " and ")) (< n 10000) (str (spell-num (/ n 1000)) " thousand" (spell-num (rem n 1000) " "))))) (defn remove-spaces-and-hyphens [word] (apply str (re-seq #"\w+" word))) (println "Answer : " (count (apply str (map #(remove-spaces-and-hyphens (spell-num %)) (range 1 1001)))))
再帰を中心に考えてみた。
spell-num の中に繰り返しパターンがあるのがちょっと気になる。うーむ。
私 manjilab のポータル的サイトになっております。日々気付いたこと、考えたこと、発表したいものを載せていきます。