0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【ひとりカレンダー】ClojureAdvent Calendar 2024

Day 8

Clojureの基本構文 - 特殊フォームその3

Last updated at Posted at 2024-12-07

advent_calendar_2024.png

Advent Calendar 2024 Day 8

基本構文

3つの基本についてまとめています

  1. リテラル
  2. 関数の定義と呼び出し
  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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?