ClojureとScala
説明だけみるとPro Scalaのほうが面白そうな気がするが両方とも欲しい気分
pythonでthreadingとmultiprocessing
Python vs Clojure – Evolving « Best in Classをみてて、「ほう」となったので、手元のmacbookで。
t_seq.py
def count(n):
while n > 0: n -= 1
count(100000000)
count(100000000)
t_thread.py
from threading import Thread
def count(n):
while n > 0: n -= 1
t1 = Thread(target=count,args=(100000000,))
t1.start()
t2 = Thread(target=count,args=(100000000,))
t2.start()
t1.join()
t2.join()
t_mprocessing.py
from multiprocessing import Process
def count(n):
while n > 0:
n -= 1
t1 = Process(target=count,args=(100000000,))
t1.start()
t2 = Process(target=count,args=(100000000,))
t2.start()
t1.join()
t2.join()
結果
$ time python t_seq.py;time python t_thread.py;time python t_mprocessing.py
real 0m17.032s
user 0m16.886s
sys 0m0.067s
real 0m37.139s
user 0m29.756s
sys 0m23.039s
real 0m8.777s
user 0m16.973s
sys 0m0.083s
Clojureのほう
user=> (defn countdown [n] (when (pos? n) (recur (dec n))))
#'user/countdown
user=> user=> (time (doall (pmap countdown (repeat 2 100000000))))
"Elapsed time: 8164.085 msecs"
(nil nil)
Programming Clojure chapter 6-9
駆け足で。
Chapter 6 (Concurrency)
STMを使う。STMはデータベースでいうACIDのうちACIを提供する。実際にいくつか書いてみないと理解できない感じ。
Chapter 7 (Macros)
マクロ
Chapter 8 (Multimethods)
マルチメソッドの話。
Chapter 9 (Cloure in the Wild)
以下の事柄に関して簡単に説明 - tet-isを使ったユニットテストについて - データベースアクセス - Compojureというweb application framework
章の最後にLancetっていうRakeみたいなソフトウェアでの実例で説明してるんだけど、Lancet使ったことないのでイメージが掴みにくかった。
もう少しClojure触ってから再度読み直すとよいかもしれん。
Programming Clojure chapter 5
どんどん読む。5章はfunctional programmingに関して。遅延評価についてもふれている。
clojureは(jvmが?)末尾最適化しないので、tail recursionでなくself recursionをすべし。
self recursionはrecurを使う。またはlazy-seqをつかって遅延評価させる。
mutual recursionの場合はtrampolineという最適化テクニックを使うとよいらしい(未消化)。
Programming Clojure chapter 4
4章はsequenceという抽象の話がメイン。
clojureではsequenceの操作関数は重要。若干関数リファレンスみたいになってしまっている部分もあるが、それは仕方ない。どうせ、実際にコード書くときに何度も見返すことになるので、lispとか触ったことがあれば軽く読み流せる章。
Programming Clojure chapter 3
3章はJava周りと最適化の話
Syntactic Sugar
javaのメソッドを呼ぶための使いやすい構文糖衣が用意されている/(slash)と.(dot)が重要
Collection
ClojureのCollectionはconcurrency-safeらしい。
Lancet
Clojureのビルドツール。これは使ってみないと読んだだけではよくわからん。
たらい回し関数
jrubyのたらい回し関数のエントリみてたらClojureでもやってみたくなった。
Clojure 1.0.0-
user=> (defn tak [x y z]
(if (>= y x)
z
(tak (tak (- x 1) y z) (tak (- y 1) z x) (tak (- z 1) x y))))
#'user/tak
user=> (time (dotimes [_ 100] (tak 24 16 8)))
"Elapsed time: 27580.504 msecs"
nil
ついでにOCamlでも
let rec tak x y z =
if y >= x then z
else tak (tak (x-1) y z) (tak (y-1) z x) (tak (z-1) x y)
;;
for a = 1 to 100 do
ignore(tak 24 16 8)
done
;;
まずはバイトコードで。
$ ocamlc tak.ml -o tak.exe
$ time ./tak.exe
real 0m10.777s
user 0m10.692s
sys 0m0.033s
ネイティブコードにコンパイルすると
$ ocamlopt tak.ml -o tak_opt.exe
$time ./tak_opt.exe
real 0m1.130s
user 0m1.119s
sys 0m0.006s
結構はやい。
そういえばMooseでLispはたらい回し関数はしるくらいまではやっとこうと思いつつ止まってしまっていたのに今気づいた。
Programming Clojure chapter 2
2章はClojureの基本的な説明。
キーワードは関数として使える。
user=> (defstruct book :title :author)
#'user/book
user=> (def b (struct book "Programming Clojure" "Stuart Halloway"))
#'user/b
user=> (b :title)
"Programming Clojure"
これは以下のようにキーワードが先にきても良い。
user=> (:title b)
"Programming Clojure"
namespacesの変更
(in-ns name)
meta-data
^が使える。
user=> ^#'str
{:ns #<Namespace clojure.core>, :name str, :file "clojure/core.clj",
:line 322, :arglists ([] [x] [x & ys]), :tag java.lang.String,
:doc "With no args, returns the empty string. With one arg x,
returns\n x.toString(). (str nil) returns the empty string.
With more than\n one arg, returns the concatenation of the str
values of the args."}
clojureでフィボナッチ
遅延評価つかうとこんなに簡単
(def fibs (lazy-cat [0 1] (map + fibs (rest fibs))))
みてみる
user=> (take 20 fibs)
(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)
Programming Clojure chapter 1
導入
定番の
user=> (defn hello [name] (str "Hello, " name))
#'user/hello
user=> (hello "kzfm")
"Hello, kzfm"
user=> (hello "clojure")
"Hello, clojure"
user=> (str *1 " and " *2)
"Hello, clojure and Hello, kzfm"
エラーが出たときにstacktrace
user=> (/ 1 0)
java.lang.ArithmeticException: Divide by zero (NO_SOURCE_FILE:0)
user=> (.printStackTrace *e)
java.lang.ArithmeticException: Divide by zero (NO_SOURCE_FILE:0)
at clojure.lang.Compiler.eval(Compiler.java:4543)
at clojure.core$eval__3990.invoke(core.clj:1728)
at clojure.main$repl__5813$read_eval_print__5825.invoke(main.clj:176)
at clojure.main$repl__5813.doInvoke(main.clj:193)
at clojure.lang.RestFn.invoke(RestFn.java:426)
at clojure.main$repl_opt__5853.invoke(main.clj:247)
at clojure.main$legacy_repl__5878.invoke(main.clj:288)
at clojure.lang.Var.invoke(Var.java:346)
at clojure.main.legacy_repl(main.java:29)
at clojure.lang.Repl.main(Repl.java:20)
Caused by: java.lang.ArithmeticException: Divide by zero
at clojure.lang.Numbers.divide(Numbers.java:138)
at user$eval__16.invoke(NO_SOURCE_FILE:6)
at clojure.lang.Compiler.eval(Compiler.java:4532)
... 9 more
nil
requireでなくてuseを使うのはpythonでいうimport some_moduleでなくて from some_module import * をしている感じ
lancetっていうビルドシステムも用意されている(RubyのRakeみたいなもの?)
Practical Clojure (The Definitive Guide)
Pro Scala: Monadic Design Patterns for the Web


Programming Clojure (Pragmatic Programmers)