Java
Clojure
Ubuntu
Leiningen
More than 5 years have passed since last update.


Ubuntu での Clojure と Leiningen 超入門

JDK は既に用意出来ているものとしますね.sudo apt-get install openjdk-7-jdk とでもしておけば大丈夫なんじゃないでしょうか? (ダメだったら教えて)

まず lein を取ってきてどこかパスの通ったところに設置します.そして実行権限を付与して実行します.

# パスとかなんのこっちゃ勢向けコピペ用コマンド

# これ実行するだけでとりあえず動かせまっせ
export PATH=$HOME/local/bin:$PATH
if [ ! -d $HOME/local/bin ]; then mkdir -p $HOME/local/bin; fi

# ダウンロード & インストール
cd $HOME/local/bin
wget https://raw.github.com/technomancy/leiningen/stable/bin/lein
chmod +x lein
./lein

はい,これで Leiningen がインストールされました.lein --version と打てばバージョンが確認出来,lein --help と打てばちょっと手間取りつつも利用可能なタスク一覧が出てきたと思います.ちなみにうちの場合バージョンは 2.3.1 でした.

この辺のことは Leiningen の Install を参考にしました.

command not found: lein 等と出たら現状間違いなく PATH の設定ミスです.

で,このタスクの中の repl というのを使っていきます.REPL (Read Eval Print Loop) とは対話型の実行環境で,Ruby や Python を触ったことのある方はお馴染みではないでしょうか?馴染みのない方もこれからすぐに馴染みます.

lein repl と打ってみて下さい.ズラズラっと REPL や Clojure のバージョン情報とかなんやかんやが表示された後,user=> という状態で止まっていると思います.ここに Clojure のコードを書いて Enter をッターンすれば,なんとそれが即座に実行されます.言語仕様を覚えたいとか,即座に何かを試したいだけとか言う時にはこれを使わない手は無いですね.

早速ここで最速入門をしてしまいましょう.

(print "Hell World")

; Hell Worldnil
; ↑これは出力されるもの.ここまでコピペしたらあかんで (してもいいけど).
; ちなみに Clojure ではコメントは ; で始まる行になる.この行もコメントです.

Oops! 地獄になってしまったしなんかよく分からないものがくっついてますね.nil です.これは,print 関数の戻り値というか評価結果が nil になったことを意味しています.最後に改行が無い様がしっかり現れていて,つまりそれを直せばいいのねと推察出来ます.

(println "Hello World")

; Hello World
; nil

やったぜ.println は行を表示する関数ですね.勝手に改行が最後に入ります.もちろん (print "Hello World\n") としても良かったということです.

ちょいと計算もしてみましょう.

(+ 1 2)

; 3
(* (+ 1 2) 3)
; 9
(* (+ 1 2) (- 3 4))
; -3

ナニコレ… となった人,「前置記法」「ポーランド記法」などと Google 先生にお伺いを立ててみて下さい.話はそれからです.

先程は評価結果が nil になっていましたが,今回は数値が出てきていますね.四則演算関数を使ったので,

もうちょっと Lisp っぽいことをしてみましょう.

(cons 1 '(2 3))

; (1 2 3)
(first '(1 2 3))
; 1
(rest '(1 2 3))
; (2 3)

Lisp の伝統的な carcdrfirstrest になりました.ぶっちゃけこの方が本来の機能を正しく表現する名前ですね.

'(1 2 3) にくっついてる ' は,評価を遅らせるものです.ここでもし評価されてしまうと,1 という関数に 2 3 を引数として与えるという意味になってしまいます.

(1 2 3)

; ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval4108 (NO_SOURCE_FILE:1)

1 は関数になれない,って感じです.

(doc cons)

(source cons)

doc 関数と source 関数はそれぞれドキュメントとその関数のソースを表示してくれます.活用しましょう.

じゃあ自分で関数を作ってみましょうか.

(fn [x y] (+ x y))

; #<user$eval4111$fn__4112 user$eval4111$fn__4112@15f3386a>

はい,引数として与えられた x y を足す関数が出来ました.

関数を作るには fn という関数を使いま… いえ実は fn は関数では無いです.マクロとか言うブツです.あんまり詳しいことは私も知りません.でも fn 関数に引数リスト渡してそれらの扱い方を指定してるみたいな感じですね.そんな風に今の私には見えます.もうちょっと達者になってくれば違う見方が出来てくるのかもしれません.

ちなみに引数リストは [] で定義します.覚えましょう.そういう仕様です.

で,せっかく作ったこの関数,残念ながらこのままでは使えません.この関数には名前が無いからです.

(defn add [x y] (+ x y))

; #'user/add

こうすれば大丈夫です.(add 1 2)3 になります.add という名前で関数が作られたので,無事 add として我々は使えるようになったのです.

待てよ…?何も関数を使ってみたいだけなら add なんて名前は不要じゃないか…?

((fn [x y] (+ x y)) 1 2)

; 3

Excellent! 出ました.無名関数です.(add 1 2)add の部分がそっくりそのまま名無しで作っただけの前述の関数に置き換わっただけです.

無名関数が出来るのか… 匿名関数は出来るのか?

((fn add-anonymous [x y] (+ x y)) 1 2)

; 3
(add-anonymous 1 2)
;CompilerException java.lang.RuntimeException: Unable to resolve symbol: add-anonymous in this context, compiling:(NO_SOURCE_PATH:1:1)

出来ました.fn は名前を付けることも出来なくはなかったということです.

ちなみにこれが出来るということは再帰関数も普通に書けるということです.

((fn f [x] (if (= x 0) 1 (* x (f (- x 1))))) 10)

; 3628800

オーケーオーケー.))))) とかふざけんなって言いたいんだろう?

こう書き直してみましょう.

(

(fn f [x]
(if (= x 0)
1
(* x (f (- x 1)))))
10)
; 3628800

repl では改行含めてコピペしても大丈夫です.

そんなことより,やっぱり ))))) あるじゃねーか!いえ違います.もはやそんなもの気にしなくていいのです.

2 つ目,つまり 2 行目にある ( に注目しましょう.これは,関数の定義が始まる位置ですね (fn の直前).つまり,それと同じ深さになっている 10 もあわせて考えると,( (なにか関数の定義) 10 ) という形になっているわけで,何かこれから関数を作って 10 を渡すのですね.

ではその「これから作る関数」をちょっと見て行きましょう.

プログラムとしてこれが出来なきゃ話にならんだろう代表選手,if の登場です.条件分岐が出来ないプログラムはただのカカシですな.

(if cond then else) という形になっています.cond を評価しそれが true なら then を,false なら else を評価して評価結果として使います.使われない方は評価されません.どういう意味か?C 言語とかの if 文と一緒ってことですよ.

そろそろ分かって来ましたね.x0 なら 1 を,そうでなければ x(- x 1) を再び自分自身 f に与えたものとの積を評価結果とする.これはまさしく階乗計算です.

インデントさえしっかり… いえ,「普通に」書けていれば,実は閉じ括弧とはそれほど重要なものでは無くなるのです.

疲れました.そろそろ for ループの話でもして終わりましょうか?

馬鹿め.for ループなぞ要らぬわ.

map, reduce, filter, nth, take, repeat, iterate この辺りの簡単な例をいくつか見てみましょう.

(map (fn [x] (+ x 1)) '(1 2 3))

(reduce + 0 '(1 2 3 4))

(filter (fn [x] (< x 3)) '(1 2 3 4))

(nth '(1 3 5 7) 2)

(take 3 '(10 20 30 40 50))

(take 5 (repeat 0))

(nth (iterate (fn [x] (+ x 2)) 0) 5)

結果は君たちの目で確かめよう!

そしてそれぞれの関数の説明は doc あるいは source 関数で確かめよう!

これらの関数を知っておくだけでも,「ああ,for とかいらんやん」と思えるはずですよ!きっと!でもまぁぶっちゃけ割とすぐに looprecur という「これ実質 for 文じゃないですかやだー!」なシロモノに遭遇すると思いますけども.

私もまだ lazy-seq とかよく分かってないし Leiningen の扱い方分からないことだらけですし,無限リストのちからってすげーくらいのノリなのでこれから勉強していきます.

フレームワークが多すぎて Web アプリ一つ作ってみようとしても結局いろんなライブラリの README 読んでばっかりです.決められません.英語つらい.

誰か 1 人でも,この記事で Clojure 初めてくれれば嬉しい限り.