Friday, June 15, 2012

Clojure入門「関数と変数」

こんにちは。ニャンパス一味の清水です。

今回はClojureの関数と変数について少しだけ解説します。

関数

関数の定義

前回も書きましたが、関数は"defn"というマクロを使って定義します。

(defn -main []
  (println "Hello, world!"))

defnの形式は以下のようになっています。

(defn 関数名 [引数 ...] 本体)

先の例の場合は関数名が-main、引数は無し、本体は(println "Hello, world!")です。

引数を取る場合には[]の中に引数の名前を並べます。

(defn add-forty-two [x]
  (+ 42 x))

(defn triangle [base height]
  (/ (* base height) 2))

それぞれ、引数をひとつ受け取って42を足した数を返す関数と、引数をふたつ受け取って三角形の面積を返す関数です。

関数の呼び出し

関数は以下のような形式で呼び出します。

(関数 引数 ...)

コードの中では括弧の中の一番左が関数かマクロになり、その右側に引数が並びます。上記で定義したadd-forty-twoを呼び出すには以下のようにします。

(add-forty-two 30)

この場合、add-forty-twoの定義内のxには30が束縛され、結果として72を返します。同様に、triangleを呼び出すには以下のようにします。

(triangle 31 6)

引数baseには31、heightには6が束縛され、93を返します。

ちなみに、Clojureのコードでは区切り文字としてスペースやタブや改行が使用されます。カンマを付けて見やすくすることもできますが、動作には影響がありません。

また、add-forty-twoやtriangleの本体でも関数を呼び出しています。

(+ x 42)

上記は + という関数を、xと42を引数として呼び出しています。

(/ (* base height) 2)

triangleではふたつの関数 / と * を呼び出しています。この場合、先に

(* base height)

が評価され、その結果と2が / の引数になります。

Javaメソッドの呼び出し

ClojureからJavaのメソッドを呼び出すことができます。Clojureの文字列はJavaのStringオブジェクトなので、Stringクラスのメソッドを使えます。

(.toLowerCase "HELLO")

上記は"HELLO"というStringオブジェクトのtoLowerCaseメソッドを呼び出しています。結果として"hello"が返ります。このように、メソッド名の前に . を付けたものを関数として呼び出すと、ひとつ目の引数のメソッドが呼び出されます。

無名関数

"fn"というマクロを使うと無名関数を作ることができます。

(fn [x] (+ 42 x))

これは先ほどのadd-forty-twoと同じ結果を返す関数です。無名関数はそのまま呼び出せるので

((fn [x] (+ 42 x)) 30)

このように、関数を置く場所に無名関数を置くことができます。(この場合意味はありませんが)

無名関数は、関数を受け取る関数を呼び出す場合などに使います。

変数 

グローバル変数

変数は"def"という特殊な形式を使用して定義できます。

(def 名前 値)

defは名前空間内でグローバルな変数を作ります。

(def x 42)

(defn add-forty-two [y]
  (+ x y))

上記の場合、add-forty-two内のxは42になります。

ローカル変数

defはグローバルな変数を作るため、関数内のローカルな変数を作る時には使用できません。ローカル変数を作るには"let"というマクロを使います。

(let [変数名 値 ...] 処理)

letで作った変数はletの中でのみ参照できます。

(let [x 42]
  (+ x 30))

上の例では + に42と30が渡されます。これを実行すると(let ...)という式自体が72を返します。(関数を作ったりはせずに)

ひとつのletで複数の変数を作ることもできます。

(let [x 42
      y 30]
  (+ x y))

これは先の例と同じ動作をします。

letで作った変数には別の値を代入できません。Clojureでは変数の再代入をしなくても大体の処理を書くことができます。再代入可能な変数が必要な場合は別の仕組みを使いますが、今回はスルーします。

関数と変数

defnは無名関数を値としてグローバル変数を定義しているだけなので、defを使って書き直すことができます。

(defn add-forty-two [x]
  (+ 42 x))

(def add-forty-two (fn [x] (+ 42 x)))

上記のdefnとdefは同じ結果になります。

つまり、Clojureでは関数も数値も同じようにデータとして扱えるということです
(JavaScriptと同じ感じです)

letを使ってローカルな関数を作ることもできますし、そのために"letfn"というマクロも用意されています。関数に関数を渡すことも多く、標準で用意されている関数には、リストやベクターを処理するために関数を受け取るものが多く存在します。

次回はその辺りも踏まえて、データ構造についてちょっぴり解説していこうと思います。

No comments:

Post a Comment