Posted at

Clojureのフォームを再確認する

More than 3 years have passed since last update.


フォームとは

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)


参考

プログラミング Clojure 第2版

Collections and Sequences in Clojure