基本構文
3つの基本についてまとめています
- リテラル
- 関数の定義と呼び出し
- 特殊フォーム
3. 特殊フォーム についてまとめています
今日は続きから書いていきます
2. 特殊フォーム
特殊フォーム(Special forms)は、Clojureコンパイラによって直接解釈されます
(fn name? [params* ] expr*)
fn
はclojureで無名関数を定義する際に使うものです。
一時的に使う関数を簡単に定義したいときや、関数名を付ける必要がない場合に便利です。
特徴としては
- オーバーロード可能(複数のアリティに対応可能)
- 再帰(recurを用いた効率的な再帰処理)
- 可変長引数(& rest-param)
それぞれの例を書いてみます
オーバーロード可能
(def overload-funciton
(fn
([] "引数がない場合のデフォルト値")
([x] (str "引数が1つ: " x))
([x y] (str "引数が2つ: " x " と " y))))
(overload-funciton) ;; => "引数がない場合のデフォルト値"
(overload-funciton "Hello") ;; => "引数が1つ: Hello"
(overload-funciton "Hello" "World") ;; => "引数が2つ: Hello と World"
ここでは引数の数によって別の動作をします
再帰
(def factorial
(fn [n]
(letfn [(helper [acc n]
(if (zero? n)
acc
(recur (* acc n) (dec n))))]
(helper 1 n))))
(factorial 5) ;; => 120
helper
という内部関数を定義し、内部でrecur
を使い、自己回帰を行っています
可変長引数
(def sum
(fn [& nums]
(reduce + nums)))
(sum 1 2 3 4) ;; => 10
(sum) ;; => 0
(sum 10 20 30) ;; => 60
& nums
は任意の数の引数をリストとして受け取ります
これらを使いこなすことで柔軟に無名関数を使うことができます
(loop [binding*] expr*)
let
に似て、バインディングを定義します。
しかし、loop
は追加で再帰ポイントを設定します。
後述のrecur
を使ってこの再起ポイントに戻すことができます。
(loop [n 5] ; 再帰ポイントを作成し、nに初期値5を設定
(when (> n 0) ; nが0より大きい間ループ
(println n) ; nを出力
(recur (dec n)))) ; nを1減らして再帰ポイントに戻る
; 5
; 4
; 3
; 2
; 1
(recur expr*)
recur
は現在の再起ポイントに戻ってそのバインディングを更新し、ループを続行します
注意点
- 引数を同じにする必要がある
- 末尾の位置で使用
(loop [n 5] ; 再帰ポイントを作成し、nに初期値5を設定
(when (> n 0) ; nが0より大きい間ループ
(println n) ; nを出力
(recur (dec n)))) ; nを1減らして再帰ポイントに戻る
; 5
; 4
; 3
; 2
; 1
通常の再帰関数ではスタックを消費しますが、
recur
はループとして実行されるので、スタックオーバーフローを防ぐことができます
(try expr* catch-clause* finally-clause?)
(try
expr* ; メインの式
catch-clause* ; 例外をキャッチするための処理
finally-clause?) ; 最後に必ず実行される処理(オプション)
catch-clause → (catch classname name expr)
finally-clause → (finally expr)
- expr*
- 評価される式
- catch-clause
- 例外を処理するための部分
-
classname
- キャッチする例外のクラス名
-
name
- キャッチした例外がバインドされる変数名
-
expr
- キャッチした例外を処理するための式
- finally-clause
- オプションで指定できる
-
try
ブロックを抜ける時に必ず実行される式
(try
(throw (Exception. "Exception occurred"))
(catch Exception e
(println "Exception caught"))
(finally
(println "This will always run")))
; Exception caught
; This will always run