Clojure

特殊形式

(def シンボル doc文字列? 初期値?)

シンボルの名前を持つグローバルvarを作成または検索し、現在の名前空間(*ns*)の値を名前空間として設定します。初期値が指定されている場合、それは評価され、varのルートバインディングは結果の値に設定されます。初期値が指定されていない場合、varのルートバインディングは影響を受けません。defは、defが呼び出された時点でvarがスレッドバインドされている場合でも、常にルートバインディングに適用されます。defはvar自身(その値ではない)を返します。シンボルが既に名前空間にあり、インターンのvarにマップされていない場合は、例外をスローします。doc文字列のサポートはClojure 1.3で追加されました。

シンボルのメタデータはすべて評価され、var自体のメタデータになります。特殊な解釈を持つメタデータキーがいくつかあります。

  • :private

    varのアクセス制御を示すブール値。このキーが存在しない場合、デフォルトのアクセスは公開(:private falseの場合と同じ)です。

  • :doc

    varの内容に関する短い(1〜3行)のドキュメントを含む文字列

  • :test

    さまざまな操作をチェックするためにassertを使用する引数のない関数。var自体は、メタデータマップ内のリテラル関数の評価中にアクセス可能です。

  • :tag

    var内のオブジェクトのJava型、またはオブジェクトが関数の場合の戻り値を示すクラス名またはClassオブジェクトの名前を表すシンボル。

さらに、コンパイラはvarに次のメタデータキーを配置します。

  • :file 文字列

  • :line 整数

  • :name 単純なシンボル

  • :ns varがインターニングされた名前空間

  • :macro varがマクロの名前である場合true

  • :arglists defnに提供された引数の形式のベクターのリスト

varのメタデータは、アプリケーション固有の用途にも使用できます。衝突を避けるために、名前空間修飾キー(例::myns/foo)の使用を検討してください。

(defn
 ^{:doc "mymax [xs+] gets the maximum value in xs using > "
   :test (fn []
             (assert (= 42  (mymax 2 42 5 4))))
   :user/comment "this is the best fn ever!"}
  mymax
  ([x] x)
  ([x y] (if (> x y) x y))
  ([x y & more]
   (reduce mymax (mymax x y) more)))

user=> (meta #'mymax)
  {:name mymax,
   :user/comment "this is the best fn ever!",
   :doc "mymax [xs+] gets the maximum value in xs using > ",
   :arglists ([x] [x y] [x y & more])
   :file "repl-1",
   :line 126,
   :ns #<Namespace user >,
   :test #<user$fn__289 user$fn__289@20f443 >}

多くのマクロはdefに展開されます(例:defndefmacro)。そのため、名前として使用されるシンボルから結果のvarのメタデータも伝達されます。

最上位以外の場所でvarのルート値を変更するためにdefを使用することは、通常、varをミュータブルなグローバル変数として使用していることを示しており、悪いスタイルと見なされます。varのスレッドローカルな値を提供するためにバインディングを使用するか、refまたはagentをvarに配置し、トランザクションまたはアクションを使用して変更することを検討してください。

(if テスト 真の場合 偽の場合?)

テストを評価します。単一の値nilまたはfalseでない場合、真の場合を評価して返します。そうでない場合、偽の場合を評価して返します。偽の場合が指定されていない場合、デフォルトはnilになります。Clojureの他のすべての条件式は、同じロジックに基づいています。つまり、nilfalseは論理的偽を構成し、それ以外はすべて論理的真を構成します。これらの意味は全体を通して適用されます。ifは、ブール値のJavaメソッドの戻り値をBooleanに変換せずに条件テストを実行します。ifは、java.lang.Booleanの任意の値ではなく、単一の値false(JavaのBoolean.FALSE)のみをテストすることに注意してください。そのため、独自のボックス化されたBooleanを作成する場合は、BooleanコンストラクタではなくBoolean/valueOfを使用してください。

(do *)

を順番に評価し、最後の式の値を返します。式が指定されていない場合、nilを返します。

(let [ バインディング* ] *)

バインディングバインディングフォーム 初期値式

バインディングフォーム内のシンボルがそれぞれに対応する初期値式またはその一部にバインドされるレキシカルコンテキストで、式を評価します。バインディングはシーケンシャルであるため、各バインディングは前のバインディングを参照できます。は暗黙のdoに含まれています。バインディングシンボルにメタデータタグが付いている場合、コンパイラはタグをクラス名に解決しようと試み、その後のバインディングへの参照でその型を想定します。最も単純なバインディングフォームはシンボルであり、これは初期値式全体にバインドされます。

(let [x 1
      y x]
  y)
-> 1

バインディング形式の詳細については、バインディング形式を参照してください。

letで作成されたローカル変数は変数ではありません。作成されると、その値は決して変化しません!

(quote フォーム)

評価されていないフォームを返します。

user=> '(a b c)
(a b c)

関数aを呼び出そうとする試みは行われません。戻り値は3つのシンボルのリストです。

(var シンボル)

シンボルはvarに解決する必要があり、Varオブジェクト自体(その値ではない)が返されます。リーダーマクロ#'x(var x)に展開されます。

(fn 名前? [パラメータ* ] *)

(fn 名前? ([パラメータ* ] *)+)

パラメータ位置パラメータ* 、または 位置パラメータ* & restパラメータ
位置パラメータバインディングフォーム
restパラメータバインディングフォーム
名前シンボル

関数を定義します(fn)。Fnは、IFnインターフェースを実装する第一級オブジェクトです。IFnインターフェースは、0〜20の範囲のアリティでオーバーロードされたinvoke()関数を定義します。単一のfnオブジェクトは、1つ以上のinvokeメソッドを実装し、アリティでオーバーロードできます。オーバーロードの1つだけが、アンパサンドの後に単一のrestパラメータを指定することで可変長にすることができます。このような可変長エントリポイントは、位置パラメータを超える引数で呼び出されると、それらをシーケンスに収集し、restパラメータによってバインドまたはデストラクチャリングします。提供された引数が位置パラメータを超えない場合、restパラメータはnilになります。

最初の形式は、単一のinvokeメソッドを持つfnを定義します。2番目は、1つ以上のオーバーロードされたinvokeメソッドを持つfnを定義します。オーバーロードのアリティは異なる必要があります。いずれの場合も、式の結果は単一のfnオブジェクトです。

は、パラメータが実際の引数にバインドされる環境でコンパイルされます。は暗黙のdoに囲まれています。名前シンボルが提供されている場合、関数オブジェクト自体に関数定義内でバインドされ、匿名関数でも自己呼び出しが可能になります。パラメータシンボルにメタデータタグが付いている場合、コンパイラはタグをクラス名に解決しようと試み、その後のバインディングへの参照でその型を想定します。

(def mult
  (fn this
      ([] 1)
      ([x] x)
      ([x y] (* x y))
      ([x y & more]
          (apply this (this x y) more))))

multなどの名前付きfnは通常、上記のようなものに展開されるdefnで定義されます。

fn(オーバーロード)は、関数の先頭に、パラメータの数(restパラメータが存在する場合を含む)に等しいアリティを持つ再帰ポイントを定義します。recurを参照してください。

fnsは、Javaの`Callable`、`Runnable`、`Comparator`インターフェースを実装します。

1.1以降

関数は、実行時の事前条件と事後条件の指定をサポートします。

関数定義の構文は以下のようになります。

(`fn` *name*? [*param* ] *condition-map*? *expr*)

(`fn` name? ([*param* ] *condition-map*? *expr*)+)

この構文拡張は、`defn`や`fn`形式に展開される他のマクロにも適用されます。

注: パラメータベクトルの後に続く唯一の形式がマップの場合、それは関数本体として扱われ、条件マップとしては扱われません。

*condition-map*パラメータを使用して、関数の事前条件と事後条件を指定できます。その形式は以下のとおりです。

{:pre [pre-expr*]
 :post [post-expr*]}

どちらのキーも省略可能です。条件マップは、引数リストのメタデータとして提供することもできます。

*pre-expr*と*post-expr*は、関数の引数を参照できるブール型式です。さらに、*post-expr*では`%`を使用して関数の戻り値を参照できます。いずれかの条件が`false`と評価され、`*assert*`がtrueの場合、`java.lang.AssertionError`例外がスローされます。

(defn constrained-sqr [x]
    {:pre  [(pos? x)]
     :post [(> % 16), (< % 225)]}
    (* x x))

バインディング形式の詳細については、バインディング形式を参照してください。

(`loop` [*binding* ] *expr*)

`loop`は`let`と全く同じですが、ループの先頭に再帰ポイントを確立します。アリティはバインディングの数に等しくなります。 `recur`を参照してください。

(`recur` *expr*)

*expr*を順に評価し、その後、並行して、再帰ポイントのバインディングを*expr*の値に再バインドします。再帰ポイントが`fn`メソッドだった場合、パラメータを再バインドします。再帰ポイントが`loop`だった場合、`loop`のバインディングを再バインドします。その後、実行は再帰ポイントに戻ります。`recur`式は、再帰ポイントのアリティと完全に一致する必要があります。特に、再帰ポイントが多変数の`fn`メソッドの先頭だった場合、`rest`引数の収集はありません。単一のシーケンス(またはnull)を渡す必要があります。テールポジション以外での`recur`はエラーです。

Clojureでは、`recur`が唯一のスタック消費のないループ構築であることに注意してください。テールコール最適化はなく、未知の範囲のループに対する自己呼び出しの使用は推奨されません。`recur`は関数型であり、テールポジションでの使用はコンパイラによって検証されます。

(def factorial
  (fn [n]
    (loop [cnt n acc 1]
       (if (zero? cnt)
            acc
          (recur (dec cnt) (* acc cnt))))))

(`throw` *expr*)

*expr*は評価され、スローされるため、`Throwable`の派生クラスのインスタンスを生成する必要があります。

(`try` *expr* *catch-clause* *finally-clause*?)

*catch-clause* → (catch *classname* *name* *expr*)
*finally-clause* → (finally *expr*)

*expr*は評価され、例外が発生しない場合、最後の式の値が返されます。例外が発生し、*catch-clause*が提供されている場合、それぞれ順番に調べられ、スローされた例外が*classname*のインスタンスである最初のものが一致する*catch-clause*とみなされます。一致する*catch-clause*がある場合、その*expr*は*name*がスローされた例外にバインドされたコンテキストで評価され、最後の式の値が関数の戻り値になります。一致する*catch-clause*がない場合、例外は関数から伝播します。正常に終了する場合も異常終了する場合も、戻る前に、すべての*finally-clause* *expr*が副作用のために評価されます。

(`monitor-enter` *expr*)

(`monitor-exit` *expr*)

これらは、ユーザーコードでは避けるべき同期プリミティブです。`locking`マクロを使用してください。

その他の特殊形式

特殊形式dot ('.')new、およびフィールドのset!については、リファレンスのJava相互運用性セクションで説明されています。

varのset!については、リファレンスのVarsセクションで説明されています。

バインディング形式(デストラクチャリング)

Clojureで最も単純な*binding-form*はシンボルです。しかし、Clojureは`let`のバインディングリスト、`fn`のパラメータリスト、そして`let`または`fn`に展開されるマクロで、デストラクチャリングと呼ばれる抽象的な構造バインディングもサポートしています。デストラクチャリングとは、類似の集合体をバインディング形式として使用して、集合体内の値へのバインディングの集合を作成する方法です。ベクトル形式は、順次集合体内の位置によってバインディングを指定し、マップ形式は、関連付けられた集合体内のキーによってバインディングを指定します。デストラクチャリング形式は、*binding-form*が存在する場所にどこでも表示でき、ネストすることもできるため、コレクションアクセサを使用するよりも明確なコードになります。

データの欠如(つまり、順次構造の要素が少なすぎる、関連付けられた構造にキーがないなど)のために対応する部分と一致しない*Binding-form*は、`nil`にバインドされます。

シーケンシャルデストラクチャリング

ベクトル*binding_form*は、ベクトル、リスト、シーケンス、文字列、配列、`nth`をサポートするもののようないコレクションの値を順番にバインドします。シーケンシャルデストラクチャリング形式は、*binding-form*のベクトルであり、`nth`を使用して検索された*init-expr*からの連続する要素にバインドされます。さらに、オプションとして、`&`の後に続く*binding-form*は、まだバインドされていないシーケンスの残りの部分(つまり、まだバインドされていない部分)にバインドされ、`nthnext`を使用して検索されます。

最後に、オプションとして、シンボルの後に`as`が続き、そのシンボルが全体の*init-expr*にバインドされます。

(let [[a b c & d :as e] [1 2 3 4 5 6 7]]
  [a b c d e])

->[1 2 3 (4 5 6 7) [1 2 3 4 5 6 7]]

これらの形式はネストできます。

(let [[[x1 y1][x2 y2]] [[1 2] [3 4]]]
  [x1 y1 x2 y2])

->[1 2 3 4]

すべてのシーケンシャルなケースにおいて、デストラクチャバインディング内の*binding-form*は、目的の値が存在するターゲットデータ構造内の場所に一致します。

アソシエイティブデストラクチャリング

マップ*binding-form*は、マップ、セット、ベクトル、文字列、配列(後者の3つは整数キーを持つ)などのコレクションで値を検索することによってバインディングを作成します。これは、*binding-form→key*ペアのマップで構成され、各*binding-form*は、指定されたキーにある*init-expr*の値にバインドされます。さらに、オプションとして、バインディング形式の`as`キーの後にシンボルが続き、そのシンボルが全体の*init-expr*にバインドされます。また、オプションとして、バインディング形式の`or`キーの後に別のマップが続き、*init-expr*に見つからない場合に、一部またはすべてのキーのデフォルト値を提供するために使用できます。

(let [{a :a, b :b, c :c, :as m :or {a 2 b 3}}  {:a 5 :c 6}]
  [a b c m])

->[5 3 6 {:c 6, :a 5}]

対応するマップキーと同じ名前のシンボルをバインドしたいことはよくあります。`keys`ディレクティブは、*binding-form→key*ペアによく見られる冗長性を処理します。

(let [{fred :fred ethel :ethel lucy :lucy} m] ...

と書くことができます。

(let [{:keys [fred ethel lucy]} m] ...

Clojure 1.6以降、マップデストラクチャリング形式でプレフィックス付きマップキーを使用することもできます。

(let [m {:x/a 1, :y/b 2}
      {:keys [x/a y/b]} m]
  (+ a b))

-> 3

プレフィックス付きキーを使用する場合、バインドされたシンボル名は、プレフィックス付きキーの右辺と同じです。`keys`ディレクティブでは、自動解決されたキーワード形式を使用することもできます。

(let [m {::x 42}
      {:keys [::x]} m]
  x)

-> 42

文字列キーとシンボルキーに一致する同様の`strs`と`syms`ディレクティブがあり、後者はClojure 1.6以降、プレフィックス付きシンボルキーも許可しています。

Clojure 1.9では、次のデストラクチャリングキー形式を使用して、同じ名前空間を共有する多くのキー(またはシンボル)を直接デストラクチャリングするサポートが追加されました。

  • `:ns/keys` - *ns*は、入力内で検索するキーのデフォルトの名前空間を指定します。

    • キー要素は名前空間を指定しないでください。

    • キー要素は、`keys`と同様に新しいローカルシンボルも定義します。

  • `:ns/syms` - *ns*は、入力内で検索するシンボルのデフォルトの名前空間を指定します。

    • syms要素は名前空間を指定しないでください。

    • syms要素は、`syms`と同様に新しいローカルシンボルも定義します。

(let [m #:domain{:a 1, :b 2}
      {:domain/keys [a b]} m]
  [a b])

-> [1 2]

キーワード引数

キーワード引数は、`akey aval bkey bval…`という形式のオプションの末尾の可変引数であり、アソシエイティブデストラクチャリングを介して関数本体でアクセスできます。また、Clojure 1.11で導入された、kwargsを受け取るように指定された関数は、キー/値のペアの代わりに、またはそれに加えて(そして後に)、単一のマップを渡すことができます。単一のマップが渡された場合、それはデストラクチャリングのためにそのまま使用され、それ以外の場合は、前のキー/値から`conj`を使用して構築されたマップに末尾のマップが追加されます。キーワード引数を受け取る関数を定義するには、*rest-param*宣言位置にマップデストラクチャリング形式を指定します。たとえば、シーケンスとオプションのキーワード引数を受け取り、値を含むベクトルを返す関数は、次のように定義されます。

(defn destr [& {:keys [a b] :as opts}]
  [a b opts])

(destr :a 1)
->[1 nil {:a 1}]

(destr {:a 1 :b 2})
->[1 2 {:a 1 :b 2}]

`destr`内の`&`の右側のマップ*binding-form*は、上記で詳しく説明されているアソシエイティブデストラクチャリング*binding-form*です。

以下の`foo`の2つの宣言は同等であり、シーケンスに対するアソシエイティブデストラクチャリングの解釈を示しています。

(defn foo [& {:keys [quux]}] ...)

(defn foo [& opts]
  (let [{:keys [quux]} opts] ...))

ネストされたデストラクチャリング

バインディング形式は互いに任意にネストできるため、ほぼ何でも分解できます。

(let [m {:j 15 :k 16 :ivec [22 23 24 25]}
      {j :j, k :k, i :i, [r s & t :as v] :ivec, :or {i 12 j 13}} m]
  [i j k r s t v])

-> [12 15 16 22 23 (24 25) [22 23 24 25]]