Clojureのメモ

関数型といわれるプログラミング言語について、広く浅く勉強している。おととしあたりから、ちびちびと、勉強にわりあててきた時間は、
Scala > Haskell == Common Lisp > Erlang > F#
というところだろうか。勉強時間は、単純に本屋で入手できた日本語の本の数に比例していると思う。Scalaは、ここ一年で本が増えた。本に限らず、どの言語でも、自分が理解できそうなら、オンラインのチュートリアルなども読んだりしている。オンラインのチュートリアルは、英語のものが多くて、自分の場合は、読むのに余計に時間がかかってしまうのが難。

いずれも、1冊から数冊の本を読んで、ちょっと手も動かして、文法をおぼえたぐらいで、Javaほどにはつかえるようになっていない。やったそばからわすれていっちゃうし。。。。^ ^;

今年は、Scalaをもっと実際に使っていきたいなー。

といっているそばから、関数型言語を広く浅く勉強中ということで、Clojureの勉強もはじめてみた。やっぱり学びはじめはパワーをつかうので冬休みみたいにまとまった時間がある時が良い。

とりあえず

プログラミングClojure

プログラミングClojure

を読んで、Clojureの勉強をはじめた。はじめの数章を読んだ感想としては、なんというか、コンパクトにまとまった内容です。そして、どんどんはなしが進んでいきます。自分には、ペースが速いかな?


ソースコードのダウンロード
http://pragprog.com/titles/shcloj/source_code
https://github.com/stuarthalloway/programming-clojure

準備

ClojureはJavaVM上で動く言語なのでJavaのインストールが必要。

ダウンロード

Clojure - Clojure Downloads
ダウンロード

Clojure本体だけでなくて、Clojure Contribもダウンロードすると良い。

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
を読んでみたけど、さっぱりわからない。とりあえず、


user=> (. java.lang.System exit 0)
なんだろうか?Java VM上で動くアプリにはちがいないので、これで終了することはする。ClojureからJavaAPIを実行するには上のような記述をするらしい。JavaのリフレクションAPIを使っているのかな。

REPLのuser=>のuserは現在の名前空間がuserであることを示している。

ドキュメント

Clojure - Sequences
関数型言語につきものの、基本的なリスト処理についてはここか。なるほど。

Clojure - Data Structures
Clojureで使えるデータ構造のようだ。このあたり、最初にちょっとでもおぼえておかないと、「Javaのあれは、Clojureどれ?」でいちいち悩みそうだ。

ClojureからJavaAPIを実行する

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
なんてあるのか。Clojurevectorつまり[....]を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=>

例外処理とかどうするんだろう。あとで調べないとな。


今度は、Javaで書いたDTOを実行してみよう。

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


JavaAPIが、かんたんに実行できることがわかった。気のせいか、直接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のあつかいもふしぎだ。例外がでてしまっているし。裏で何かをやってくれてるんだろうけど、自分が何をしたらうまくいくのかわからない。

その他メモ