Clojure 入門

2023/03/05に公開

Clojure 入門

概要

初めてClojureに触れる人の手助け、また自分の振り返りとして記事をまとめています。
Clojure新米であるため、誤りがあれば都度ご連絡いただけると幸いです。

概要から始まり、基礎 -> 関数型プログラミング -> WEBアプリという順番でまとめていく想定です。
※ 基本的には、Clojure公式ドキュメントを閲覧することをお勧めします。

※ 注意: 著者はLispや関数型言語を取得しているわけではございません。過不足ございましたらご指摘お願いいたします。

Clojureとは・・・・

Clojureは、Javaプラットフォーム上で動作するLisp方言の一つで、2007年にRich Hickeyによって開発されました。

Clojureは、Lispの影響を受けた関数型プログラミング言語であり、Javaとのシームレスな統合を可能にするために、Java仮想マシン(JVM)上で実行されます。

Clojureの特徴として、以下のようなものが挙げられます。

  • 関数型プログラミングのサポート:Clojureは、高階関数、無名関数、再帰関数、マクロなどの機能を提供しています。
  • 不変性データ構造:Clojureは、永続的な不変性データ構造をサポートしています。これにより、副作用のない関数を記述しやすくなり、安全で並行処理に適したプログラムを開発することができます。
  • マクロ:Clojureは、マクロ機能を提供しており、コードの自動生成や再利用を簡単にすることができます。
  • Javaとの統合:Clojureは、Javaとの相互運用性を備えており、Javaクラスやライブラリを直接利用することができます。
  • 高速性:Clojureは、JVM上で動作するため、高速で効率的なプログラムを開発することができます。

※ ClojurescriptというClojureのJavascriptコンパイラもあり、Web開発におけるフロント側の実装を行うこともできます。

Clojure基礎

clojureを利用するにあたり、Clojureにおける基礎概念について紹介します。

  1. データ型

Clojureには、数値、文字列、シンボル、キーワード、リスト、ベクター、マップ、集合などのデータ型があります。
これらのデータ型は、それぞれ独自の特徴を持っています。

  • 数値: 整数、浮動小数点数、比較演算子などが利用できます。
  • 文字列: テキストを表現するためのデータ型で、ダブルクォーテーションで囲みます。
  • シンボル: Clojureにおける変数や関数の名前のようなもので、先頭にシングルクォーテーションを付けたもので表現されます。
  • キーワード: シンボルの先頭にコロンを付けたもので、一般的にマップのキーとして使用されます。
  • リスト: 複数の要素を含むデータ型で、丸括弧で囲みます。
  • ベクター: リストと同様に複数の要素を含むデータ型で、角括弧で囲みます。リストとの違いは、ベクターは要素にアクセスする際に高速である点です。
  • マップ: キーと値のペアを持つデータ型で、波括弧で囲みます。
  • 集合(set): 重複を許さず、順序を持たないデータ型で、波括弧で囲みます
  • 正規表現: #"[正規表現]"
  • UUID: #uuid "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
  • アトム: 値が不変な変数を表現するデータ型で、変数の値を更新することはできません。
  1. 変数

Clojureには変数の概念がありますが、Clojureでは変数を定義すると、その変数は不変(再代入を許さない)となります。

変数の値を変更するためには、新しい変数を定義する必要があります。

Clojureでの変数の定義には、letやdefなどの特別な形式があります。

letはローカルな変数を定義し、defはグローバルな変数を定義します。

例えば、次のように変数を定義することができます。

(let [x 1

y 2]

(+ x y))

上記の例では、xとyという変数を定義しています。また、2つの変数を足し合わせています。

  1. 関数

Clojureでは、関数が非常に重要な役割を担っています。

関数は値を返すことができ、他の関数に渡すことができます。また、関数は一等公民であるため、関数自体を変数に代入したり、他の関数から返されたりすることができます。

Clojureの関数は、関数名の後に引数を丸括弧で囲んで定義します。また、Clojureの関数は多くの場合、再帰呼び出しを使って実装されます。

例えば、次のように関数を定義することができます。

(defn add [x y]

(+ x y))

また、Clojureでは無名関数を定義することもできます。無名関数は、#(式)という形式で表現されます。

例えば、次のように無名関数を定義することができます。

#(+ %1 %2)
  1. 名前空間

Clojureでは、名前空間という概念があります。名前空間を使うことで、異なる名前空間で同じ名前の変数や関数を定義することができます。

名前空間を使うことで、コードの重複を避けたり、モジュール化したりすることができます。

名前空間は、nsという特別な形式を使って定義します。

例えば、次のように名前空間を定義することができます。

(ns my-namespace

(:require [clojure.string :as str]))

上記の例では、my-namespaceという名前の名前空間を定義しています。また、:requireを使って、clojure.stringという名前空間をインポートしています。

  1. マクロ

Clojureには、マクロという概念があります。マクロを使うことで、プログラムの実行前にコードを変換することができます。これによって、コードの簡略化や最適化などを行うことができます。

マクロは、defmacroという特別な形式を使って定義します。マクロは、関数と同様に引数を受け取りますが、関数と異なり、マクロは式を返すのではなく、式を生成します。

例えば、次のようにマクロを定義することができます。

(defmacro my-if [cond then else]

`(if ~cond ~then ~else))

上記の例では、my-ifというマクロを定義しています。このマクロは、3つの引数を受け取り、if式を生成して返します。マクロの本体は、バッククォートと呼ばれる特殊な記号を使って式を記述しています。バッククォートの中で、~を使って変数を展開しています。

  1. コレクション

Clojureには、リストやベクターなど、多くのコレクション型があります。これらのコレクション型は、データを管理するための非常に強力な手段です。

  • リスト: '(1 2 3)

  • ベクター: [1 2 3]

  • マップ: {:name "Alice" :age 25}

  • セット: #{1 2 3}

リストとベクターは、要素の追加や削除に対して、それぞれ最適化されています。また、マップは、キーと値のペアを格納するために使用され、セットは、一意の値のコレクションを格納するために使用されます。

これらの操作には、要素の追加、削除、検索、繰り返し処理などが含まれます。

  1. シーケンス

Clojureには、シーケンスという概念があります。シーケンスは、データのストリームを表現するために使用されます。

シーケンスは、任意の数の要素を持つことができ、必要に応じて、遅延評価されることができます。

シーケンスは、seqという関数を使って生成することができます。また、firstやrestなど、シーケンスの要素にアクセスするための関数が提供されています。

Clojureのシーケンスには、リストやベクター、マップ、セットなどが含まれます。それらは、高度に柔軟なものであり、様々な操作が可能です。

例えば、シーケンスをフィルタリングしたり、マッピングしたり、並び替えたりすることができます。

  1. プロトコル

Clojureには、プロトコルという機能があります。

プロトコルは、特定のデータ型に対して、インターフェイスを定義するために使用されます。これにより、異なるデータ型に対して同じ操作を実行することができるようになります。

例えば、clojure.core/seqableというプロトコルは、シーケンスのように振る舞うことができるすべてのデータ型に対して、seq関数を提供します。

これにより、異なるデータ型に対して同じ操作を実行することができます。

※ JavaのInterfaceに近いイメージです。

(defprotocol Flyable

(fly [obj]))



(defrecord Bird [name]

Flyable

(fly [obj]

(println (str (:name obj) " is flying."))))



(defrecord Airplane [model]

Flyable

(fly [obj]

(println (str (:model obj) " is flying."))))



(fly (Bird. "Eagle")) ; "Eagle is flying."

(fly (Airplane. "Boeing 747")) ; "Boeing 747 is flying."
  1. Javaのインターフェース

Clojureは、Javaとのインターフェイスが可能です。これによって、Javaで書かれたライブラリやフレームワークをClojureで使うことができます。

Javaのクラスやメソッドにアクセスするには、Javaのパッケージ名を指定した上で、Javaのクラスやメソッドを呼び出す形式を使います。

例えば、次のようにJavaのクラスをインポートして、Javaのメソッドを呼び出すことができます。

(import 'java.util.Date)



(defn print-current-date []

(let [date (Date.)]

(println date)))

上記の例では、java.util.DateというJavaのクラスをインポートして、Dateクラスのインスタンスを作成し、現在の日付を表示しています。

※ Date.は、JavaのDateクラスのコンストラクタを呼び出すためのClojureの書き方です。

JavaのクラスをClojureで使う場合、クラス名の後に.をつけて、インスタンス化するためのコンストラクタを指定します。

練習問題
※ 解答例はあくまで解答例ですので、参考程度に参照ください。

関数型プログラミング

WEB アプリケーション

下記を参考にするといいかと思います。

※ 私の方でもまとまり次第、記載していきます。

Clojure の日本語ガイド — Clojure の日本語ガイド

Discussion