フォームとは
Clojureのプログラムはまずリーダがプログラムをフォームという単位毎に読み込み、それをClojureのデータ構造に変換し、そのデータ構造をコンパイルして実行します。
今回はClojureの基本パーツであるフォームがどういうものか、どういう種類があるかを一通り確認していきたいと思います。
フォームの種類一覧
フォーム | 例 |
---|---|
リスト | (1 2 3), (str "hello" \space "world") |
ブール値 | true, false |
Nil | nil |
文字 | \a, \b |
文字列 | "hello" |
シンボル | user/foo, java.langString |
数値 | 1, 2, 33 |
キーワード | :tag, :doc |
マップ | {:name "Yuji" :gender "male"} |
セット | #{:ruby :clojure :javascript} |
ベクタ | [1 2 3] |
リスト
リストは単なるデータであるが、関数呼び出しの構文としても使われる。最初の要素が関数名、その次の要素からはその関数の引数になる。
(+ 1 2 3)
;; => 6
(str "hello" "world")
;; => "helloworld"
ブール値とnil
Clojureにおけるブール値の規則は以下になります。
- trueが真, falseが偽
- falseの他に、nilもブール値が必要な場面では偽
- false,nil以外は、ブール値が必要な場面では真
(if true "true" "false") ;;=> "true"
(if false "true" "false") ;;=> "false"
(if nil "true" "false") ;;=> "false"
(if [] "true" "false") ;;=> "true"
(if "hello" "true" "false") ;;=> "true"
文字
文字のリテラル表記は{letter}で、{letter}は文字そのものか文字の名前になる。
(str \h \e \l \l \o \space \w \o \r \l \d)
;; => "hello world"
文字列
文字列はダブルクォートで囲んで表記し、複数行にまたがることもできる
"hello world"
;;=> "hello world"
"hello
world"
;; => "hello\n world"
シンボル
+, concat , java.lang.Stringといったフォームはシンボルといい、ものを名付けるのに使われる。例えば+は加算する関数の名前として使われている。
;; "hello"とプリントする関数をsay-helloというシンボルに束縛している
(def say-hello (fn [] (println "hello")))
(say-hello)
;;=> "hello"
;;=> nil
数値
数値はその値自身へと評価されるため、REPLに打ち込むとそれがそのまま返される。
57
;; => 57
算術演算子も以下のように使える。
(- 2 1)
;; => 1
(+ 2 1)
;; => 3
(* 2 2)
;; => 4
(/ 4 2)
;; => 2
(/ 22 7)
;; => 22/7
(/ 22.0 7)
;; => 3.142857142857143
;; 商を求める
(quot 22 7)
;; => 3
;; 余りを求める
(rem 22 7)
;; => 1
キーワード
キーワードはシンボルに似ているが、コロン(:)で始まる。そして評価されると自分自身を返す。
自分自身を返すという特性上、キーワードはマップのキーによく使われる。
:tag
;;=> :tag
(def person {:name "yuji" :gender 'male'})
(person :name)
;; => "yuji"
(:name person)
;; => "yuji"
マップ
マップはキーと値のペアのコレクションである。マップはカーリブレース({})でくくって、リテラルで表記できる。
マップは関数としても動作する。キーをマップに引数として渡すと、対応する値があれば値を返し、なければnilを返す。
(def inventors {:Lisp "McCarthy" :Clojure "Hickey"})
;; => #'user/inventors
(inventors :Lisp)
;; => "McCarthy"
(inventors :Ruby)
;; => nil
セット
セットはコレクションの各要素がユニークであることを保証したコレクションです。セットはシャープカーリブレース(#{})でくくって、リテラルで表記できる。
セットに対して値を与えると、対応する値があればその値を返し、なければnilを返す。
(def languages #{"Ruby" "Lisp" "Clojure"})
;; => #'user/languages
(language "Ruby")
;; => "Ruby"
(language "PHP")
;; => nil
ベクタ
ベクタはインデックスによってアクセスできるコレクションです。ベクタはスクエアブラケットでくくって、リテラルで表記できる。
(def language ["Ruby" "Lisp" "Clojure"])
;; => #'user/language
(language 0)
;; => "Ruby"
(language 1)
;; => "Lisp"
(language 9)
;; => IndexOutOfBoundsException clojure.lang.PersistentVector.arrayFor (PersistentVector.java:107)