On Github kaosf / 20140209-clojure-getting-started
JVM の上で動く言語の一つ
Lisp
これから Leiningen というツールを使ってClojure という言語での
などの操作を行う
Leiningen は他にも依存しているライブラリをインストールしたり実行可能なファイルを作成したり出来る
プラグインで機能拡張も出来る
Ubuntu
sudo aptitude install openjdk-7-jdk
Fedora
sudo yum install -y java-1.7.0-openjdk java-1.7.0-openjdk-devel
他のディストリビューションでもOpenJDK 7 をインストールすれば OK
Windows or Mac
Oracle のサイトから DL & Install
Linux or Mac
cd $HOME/local/bin wget https://raw.github.com/technomancy/leiningen/stable/bin/lein chmod +x lein ./lein
何処か PATH の通ったところに lein をダウンロードして実行権限を与えて実行
Windows
ここに従えば OK
JDK のインストールも必要です
世界で二番目に古いプログラミング言語
S-Expression (S 式) で記述する
S は Symbol
前置記法とも言う
関数 (演算子も関数と考える) を前置する
我々が普段算数・数学で使うのは中置記法
中置: 1 + 2
前置: (+ 1 2)
中置: 1 + 2 * 3
前置: (+ 1 (* 2 3))
演算子の優先順位を考える必要が無い
f という関数があり整数二つを引数にとるとする
数学・C 言語など: f(1, 2)
ポーランド記法: (f 1 2)
コードとデータが等価
マクロという強力な機能(いわゆる C 言語等のマクロとは出来ることのレベルが違う)
REPL (Read Evaluate Print Loop) を使う
lein repl
で起動する
Windows の方はアプリケーションとしても用意されている
REPL で
(+ 1 2)
と打ってみよう
3
が返ってくる
(* 2 3)
これは当然
6
になる
(+ (* 3 4) 5)
3 掛ける 4 の 12 に 5 を足して
17
となる
a.clj を用意して
(+ (+ 1 (* 2 3)) (* 4 5))
とでも書いておく
これは (1 + (2 * 3)) + (4 * 5) 相当である
(load-file "a.clj")
とすれば OK
27
になったはず
(def x 10)
これで 10 という値に x が束縛された
x ;-> 10
これで x 自体が評価される
(+ x 20) ;-> 30
これは (+ 10 20) と同等である
; から行末まではコメントである
;-> 10 と書いてあるのは評価した結果が 10 になったということ
この書き方は他の言語でもよくある
(defn f [x] (+ x 1))
これで引数 x に 1 を足す関数 f が定義された
(f 2) ;-> 3
先ほどの説明のようにこれでf に引数として 2 を渡すことになる
(defn g [x y] (+ (* x 10) y))
2 引数以上の関数は上記のように定義する
(g 1 2) ;-> 12
これで x に 1, y に 2 が渡される
(fn [x] (+ x 1))
名前の無い関数
もしもその関数に f という名前が付いていれば
(f 1)
で良い
ならば
((fn [x] (+ x 1)) 1) ;-> 2
で良い
'(1 2 3 4)
これで 1 から 4 までの要素が並んだリストが表現出来る
( の前についているクオートは評価を抑制するためのもの
普通に (1 2 3 4) としてしまうと 1 という関数に2 3 4 を引数として渡すと解釈される
1 は関数ではないのでエラーになる
(first '(1 2 3 4)) ;-> 1
これでリストの先頭の 1 が取れる
(rest '(1 2 3 4)) ;-> (2 3 4)
これでリストの先頭以外の残り (2 3 4) が取れる
他の Lisp ではこの関数は car cdr として存在したりする
(cons 1 '(2 3 4)) ;-> (1 2 3 4)
1 つの要素とリストを連結して新しいリストを作る
cons は construct の略
(1 2 3 4)
というリストは
(cons 1 (cons 2 (cons 3 (cons 4 '()))))
という形になっている
first と rest は (cons x y) について
(first (cons x y)) ;-> x (rest (cons x y)) ;-> y
という関係を持っている
nth という関数がある
(nth '(10 20 30 40) 2) ;-> 30
2 番目だけは second という関数もある
(second '(10 20 30 40)) ;-> 20
(if true 1 2) ;-> 1
(if false 3 4) ;-> 4
(if (= 1 2) 10 20) ;-> 20
全要素に特定の関数を適用した結果のリストを得る
(map (fn [x] (* x 2)) '(1 2 3)) ;-> (2 4 6)
全要素を特定の関数で畳み込む
(reduce + 0 '(1 2 3)) ;-> 6
(+ (+ (+ 0 1) 2) 3)
と等価になる
※実際は + は可変長引数関数であり (+ 1 2 3) で 6 になる
(reduce (fn [x y] (+ (* x 10) y)) 0 '(1 2 3))
これは
(defn f [x y] (+ (* x 10) y)) (f (f (f 0 1) 2) 3)
と等価なので 123 となる
全要素を特定の関数でフィルタリングして残ったもののみで構成されたリストを得る
(filter (fn [x] (>= x 5)) '(1 2 3 4 5 6 7 8 9)) ;-> (5 6 7 8 9)
遅延評価の仕組みのおかげで無限長のリストを扱える
(repeat 1) ;-> (1 1 1 1 1 1 1 ...)
このままでは REPL が応答しなくなる (Ctrl + C で中断可能)
与えられた引数を無限に要素に持つリストを返す
(repeat 10) ;-> (10 10 10 ...)
与えられた引数のリストを繰り返す無限長リストを返す
(cycle '(1 2 3)) ;-> (1 2 3 1 2 3 1 2 3 ...)
与えられた関数を繰り返し適用して得られる要素が並ぶリストを返す
(iterate (fn [x] (* x 10)) 1) ;-> (1 10 100 1000 10000 ...) (iterate (fn [x] (+ x 1)) 0) ;-> (0 1 2 3 4 ...)
(take 5 (repeat 1)) ;-> (1 1 1 1 1) (take 10 (cycle '(3 2 1))) ;-> (3 2 1 3 2 1 3 2 1 3)
もちろん take は有限長のリストにも使える
(take 5 '(1 2 3 4 5 6 7)) ;-> (1 2 3 4 5)
"abc" ;-> "abc"
(str "abc" "def") ;-> "abcdef"
str という文字列を作る関数がある
(str 1) ;-> "1"
(println "Hello World!")
println は文字列を行として標準出力に表示する関数
集合
#{1 2 3} ;-> #{1 2 3}
(set '(1 2 3)) ;-> #{1 2 3}
(conj #{1 2 3} 4) ;-> #{1 2 3 4} (conj #{1 2 3} 1) ;-> #{1 2 3}
集合なので同じ要素は 2 つ以上無い
conj は conjunction
{:a 1 :b 2}
key と value で作られた hash-map
上記の例は :a というキーの値が 1,:b というキーの値が 2 のマップである.
(hash-map :a 1 :b 2) ;-> {:a 1 :b 2}
上記のような作り方もある
:a ;-> :a
: に文字列が続く形のものがキーワード
文字列のようなもの
(keyword "a") ;-> :a
このような作り方もある
キーを指定して値を取り出す
(:a {:a 1 :b 2}) ;-> 1
Clojure ではキーワードが Map を引数にとる関数のように振る舞える
({:a 1 :b 2} :a) ;-> 1
Map もキーワードを引数にとる関数のように振る舞える
ここから実用的な言語の話をします
Leiningen でアプリケーションのプロジェクトを作成
lein new app myapp
cd myapp lein uberjar
java -jar target/myapp-0.1.0-SNAPSHOT-standalone.jar
Hello, World!
src/myapp/core.clj がソースファイル
-main という関数が main 関数
ここから起動する
(ns myapp.core (:gen-class)) (defn -main "I don't do a whole lot ... yet." [& args] (println "Hello, World!"))
次のように改造
(ns myapp.core (:gen-class)) (defn -main "I don't do a whole lot ... yet." [x] (println "Hello, World!") (println "The argument \"x\" is " x))
java -jar target/myapp-0.1.0-SNAPSHOT-standalone.jar 10
Hello, World! The argument "x" is 10
math.combinatorics というライブラリがある
数学での順列・組み合わせを扱うライブラリ
project.clj を編集する
(defproject myapp ... ... :dependencies [[org.clojure/clojure "1.5.1"]] ...)
以下のように編集
(defproject myapp ... ... :dependencies [[org.clojure/clojure "1.5.1"] [org.clojure/math.combinatorics "0.0.6"]] ...)
combinations という関数にリストと要素数を渡すとその組み合わせを列挙したリストを返してくれる
たとえば (combinations '(1 2 3) 2) は((1 2) (1 3) (2 3)) と評価される
2 引数を受け取り xCy を計算してみる
以下の様に改造
(ns myapp.core (:gen-class) (:require [clojure.math.combinatorics :as combo])) (defn -main "I don't do a whole lot ... yet." [x y] (println "Hello, World!") (println (str "x = " x ", y = " y ", xCy = " (count (combo/combinations (range 0 (Integer. x)) (Integer. y))))))
(:require [clojure.math.combinatorics :as combo])
これは今は詳しく説明しません
範囲からリストを作成して返す
(range 0 10) ;-> (0 1 2 3 4 5 6 7 8 9) (range 1 5) ;-> (1 2 3 4)
リストの長さ (要素の数) を返す
(count '(1 1 1 1)) ;-> 4 (count '()) ;-> 0
(Integer. "123") ;-> 123
とりあえずここでは文字列を数字に変えるために使う
こうやって使うもんだと今日だけ覚えて下さい
引数で受け取った x y は文字列なので整数に直し
range でリストを作り
combinations で組み合わせのリストを取得し
その数を数える
lein uberjar java -jar target/myapp-0.1.0-SNAPSHOT-standalone.jar 10 2
Hello, World! x = 10, y = 2, xCy = 45
lein new default mylib
src/mylib/core.clj に以下を追加
(defn f [x] (+ x 1))
lein install
これでホームディレクトリ (場所は環境によりけり) の.m2 というディレクトリにインストールされる
m2 という名前は Maven 由来
project.clj の :dependencies に以下を追加
[mylib/mylib "0.1.0-SNAPSHOT"]
src/myapp/core.clj を以下のように変更
(ns myapp.core (:gen-class) (:require [clojure.math.combinatorics :as combo]) (:require [mylib.core :refer :all])) (defn -main "I don't do a whole lot ... yet." [x y] (println "Hello, World!") (println (str "x = " x ", y = " y ", xC(y + 1) = " (count (combo/combinations (range 0 (Integer. x)) (f (Integer. y)))))))
lein uberjar java -jar target/myapp-0.1.0-SNAPSHOT-standalone.jar 10 1
Hello, World! x = 10, y = 1, xC(y + 1) = 45
ごめんなさい説明しないのではなく
出来ません
Clojure まだまだ初心者なんです
Ring とは
Ruby でいう Rack
Python でいう WSGI (こっちはよく知りませんが…)
Ring で 0 から構築するのは勉強になる
が時間が掛かりそうなので少しだけ楽をする
Compojure というライブラリを使う
lein new compojure mycompojureapp
lein ring server-headless
ブラウザで http://localhost:3000 にアクセス
lein ring server
ブラウザも自動で開く
(GET "/hoge" [] "GET /hoge")
自動でソースも再読み込みされる
(GET "/hoge/fuga" [] "GET /hoge/fuga")