Clojureのメモ
関数型といわれるプログラミング言語について、広く浅く勉強している。おととしあたりから、ちびちびと、勉強にわりあててきた時間は、
Scala > Haskell == Common Lisp > Erlang > F#
というところだろうか。勉強時間は、単純に本屋で入手できた日本語の本の数に比例していると思う。Scalaは、ここ一年で本が増えた。本に限らず、どの言語でも、自分が理解できそうなら、オンラインのチュートリアルなども読んだりしている。オンラインのチュートリアルは、英語のものが多くて、自分の場合は、読むのに余計に時間がかかってしまうのが難。
いずれも、1冊から数冊の本を読んで、ちょっと手も動かして、文法をおぼえたぐらいで、Javaほどにはつかえるようになっていない。やったそばからわすれていっちゃうし。。。。^ ^;
今年は、Scalaをもっと実際に使っていきたいなー。
といっているそばから、関数型言語を広く浅く勉強中ということで、Clojureの勉強もはじめてみた。やっぱり学びはじめはパワーをつかうので冬休みみたいにまとまった時間がある時が良い。
本
とりあえず
- 作者: Stuart Halloway,川合史朗
- 出版社/メーカー: オーム社
- 発売日: 2010/01/26
- メディア: 単行本(ソフトカバー)
- 購入: 10人 クリック: 338回
- この商品を含むブログ (72件) を見る
ソースコードのダウンロード
http://pragprog.com/titles/shcloj/source_code
https://github.com/stuarthalloway/programming-clojure
ダウンロード
Clojure - Clojure Downloads
ダウンロード
REPLの起動
インストールは、特にやることなし。
ダウンロードしたファイルを解凍するだけ。
Clojure - Getting Started
Clojureの実行方法が書いてある。ダウンロードして、jarにクラスパスを通すだけで良いようだ。
Clojure本体だけでなくて、Clojure Contribもクラスパスをとおしておくと良い。
REPLを使いやすくするためにJLine - JLineもつかうと良いらしい。ダウンロードして、jline-0.9.94\jline-0.9.94.jarもクラスパスに含める。
Windows版の起動スクリプトは、こんなんで良いか。
clojure-repl.cmd(バージョンは2011/8/7時点の最新)
@echo off set JAVA_HOME="C:\Program Files\Java\jdk1.7.0" set JAVA=%JAVA_HOME%\bin\java.exe set CLOJURE_ROOT=C:\Application\ProgrammingLanguage\Clojure set CLOJURE_HOME=%CLOJURE_ROOT%\clojure-1.2.1 set CLOJURE=%CLOJURE_HOME%\clojure.jar set CLOJURE_CONTRIB=%CLOJURE_ROOT%\clojure-contrib-1.2.0\target\clojure-contrib-1.2.0.jar set JLINE_HOME=%CLOJURE_ROOT%\jline-1.0 set JLINE=%JLINE_HOME%\jline-1.0.jar set CLASSPATH=%CLOJURE%;%CLOJURE_CONTRIB%;%JLINE% cmd /K "%JAVA% -cp %CLASSPATH% jline.ConsoleRunner clojure.main"
それで、clojure-repl.cmdを実行すれば、
Clojure 1.2.0
user=>
起動できた。
Clojure 1.2.0
user=> (println "Hello, World!")
Hello, World!
nil
user=>
ClojureのREPLの終了方法がわからないな。
clojure-1.2.0\src\clj\clojure\main.clj
を読んでみたけど、さっぱりわからない。とりあえず、
なんだろうか?Java VM上で動くアプリにはちがいないので、これで終了することはする。ClojureからJavaのAPIを実行するには上のような記述をするらしい。JavaのリフレクションAPIを使っているのかな。
user=> (. java.lang.System exit 0)
REPLのuser=>のuserは現在の名前空間がuserであることを示している。
ドキュメント
Clojure - Sequences
関数型言語につきものの、基本的なリスト処理についてはここか。なるほど。
Clojure - Data Structures
Clojureで使えるデータ構造のようだ。このあたり、最初にちょっとでもおぼえておかないと、「Javaのあれは、Clojureどれ?」でいちいち悩みそうだ。
ClojureからJavaのAPIを実行する
Clojure - Java Interop
あたりを参考にして、実行してみると、
user=> (java.text.MessageFormat/format "{0,number,0000}" (into-array [12]))
"0012"
user=> (. java.text.MessageFormat format "{0,number,0000}" (into-array [12]))
"0012"
into-array
なんてあるのか。Clojureのvectorつまり[....]をJavaの配列に変換してくれるようだ。
user=> (. (new java.util.Date) getTime)
1294415567179
user=> (def testMap (new java.util.HashMap))
#'user/testMap
user=> (. testMap put 1 "OK")
nil
user=> (. testMap get 1)
"OK"
user=> (import 'java.util.TreeSet)
java.util.TreeSet
user=> (def testTreeSet (new TreeSet))
#'user/testTreeSet
user=> (. testTreeSet add "b")
true
user=> (. testTreeSet add "c")
true
user=> (. testTreeSet add "a")
true
user=> (. testTreeSet add "a")
false
user=> (def it (. testTreeSet iterator))
#'user/it
user=> (. it next)
"a"
user=> (. it next)
"b"
user=> (. it next)
"c"
user=> (. it next)
java.util.NoSuchElementException (NO_SOURCE_FILE:0)
user=>
user=> (import 'java.io.File)
java.io.File
user=> (. (new File "C:/Users/Eiichi/Desktop/test.txt") exists)
true
user=> (import 'java.io.FileInputStream)
java.io.FileInputStream
user=> (def fis (new FileInputStream "C:/Users/Eiichi/Desktop/test.txt"))
#'user/fis
user=> (. fis read)
116
user=> (. fis read)
101
user=> (. fis read)
115
user=> (. fis read)
116
user=> (. fis read)
−1
user=> (. fis close)
nil
user=>
例外処理とかどうするんだろう。あとで調べないとな。
package myapp.java.examples; public class Person { private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setAge(Integer age) { this.age = age; } public Integer getAge() { return this.age; } } このクラスをコンパイルして、クラスパスをとおして、clojureを起動する。
user=> (def p (new myapp.java.examples.Person "eiichi" 35))
#'user/p
user=> (bean p)
{:name "eiichi", :class myapp.java.examples.Person, :age 35}
user=> (def p1 (bean p))
#'user/p1
user=> p1
{:name "eiichi", :class myapp.java.examples.Person, :age 35}
user=> (get p1 :name)
"eiichi"
user=> (get p1 :age)
35
user=> (p1 :name)
"eiichi"
user=> (p1 :age)
35
user=> (:name p1)
"eiichi"
user=> (:age p1)
35
JavaのAPIが、かんたんに実行できることがわかった。気のせいか、直接Javaのソースコードを書くより、Javaをかんたんに使えるような。
今度は、もっと、Clojureっぽいことするにはどうするのだろう?
Clojureのデータ構造
とりあえず、データ構造を表現するAPIを調べてみよう。
基本的なデータ構造を表現するAPIは、比較的メジャーな言語なら、どの言語でも、似たり寄ったりのものが用意されているはず。
record
recordは、たしかHaskellもあったな。
ScalaのTupleは、._1とか名前でなくて、番号で指定だった。
user=> (defrecord Person [name age])
user.Person
user=> (user.Person. "eiichi" 35)
#:user.Person{:name "eiichi", :age 35}
user=> (def p2 (user.Person. "eiichi" 35))
#'user/p2
user=> (get p2 :name)
"eiichi"
user=> (get p2 :age)
35
user=> (p2 :name)
java.lang.ClassCastException: user.Person cannot be cast to clojure.lang.IFn (NO
_SOURCE_FILE:0)
user=> (p2 :age)
java.lang.ClassCastException: user.Person cannot be cast to clojure.lang.IFn (NO
_SOURCE_FILE:0)
user=> (:name p2)
"eiichi"
user=> (:age p2)
35
user=>
defrecord使い方がイマイチはっきりしない。ドキュメントもみたんだけどな。userはデフォルトの名前空間なんだろうか?user.Person.の.のあつかいがふしぎですなー。キーワード:xxxxのあつかいもふしぎだ。例外がでてしまっているし。裏で何かをやってくれてるんだろうけど、自分が何をしたらうまくいくのかわからない。