Clojure
Clojure (発音は/'klouʒər/[3], クロージャー)は、関数型プログラミング言語であり、LISP方言の一つである。関数型プログラミングのプログラミングスタイルでのインタラクティブな開発を支援し、マルチスレッドプログラムの開発を容易化する汎用言語である。ClojureのプログラムはJava仮想マシンで動作する。.NETで動作するClojureCLRも開発されている。Clojureは「データとしてのプログラムコード」 (英語:「code as data」) という思想で設計されており、洗練されたマクロ機構を持つ。 設計思想リッチ・ヒッキー (Rich Hickey)がClojureを設計した目的は、既存のJavaプラットフォーム上で動作して、並行コンピューティングができる、関数型のLISP系の言語を作ることである。[4][5] Clojureが並行コンピューティングを実現する手法は、不変(イミュータブル)な状態の連鎖という概念によって特徴づけられる。 [6]状態が不変であるため、ひとつの状態に対して複数の操作を並列に行うことができ、並列性という問題が「状態遷移の管理」になる。そのため、Clojureには、状態遷移に関して明確な定義をもつ可変な参照型がいくつか用意されている。 文法他のLISP系言語と同様、ClojureのプログラムはS式で表現する。プログラムコードはコンパイルされる前に、リーダーによって解析され、内部データ構造に変換される。Clojureのリーダーは、リストの他に配列・ハッシュテーブル・集合もリテラル表現として扱うことができ、そのままコンパイラに渡される。言いかえると、Clojureのコンパイラはリストだけでなく、上に挙げた全てのデータ構造を直接扱うことができるということである。ClojureはLisp-1であり、関数名と変数名は同一名前空間にある。また、他のLISP系言語とのコードの互換性は考慮されていない。 マクロClojureのマクロ機構は、Common Lispのそれによく似ている。ただし、Clojureのバッククオート(「シンタクス・クオート」と呼ばれる)では、個々の記号が局所的な名前空間によって区別されるという点で、Common Lispのマクロとは異なっている。この仕組みによって、マクロ展開時の変数の捕獲(= マクロが展開された環境に同名の変数があると、その変数の値が変更されてしまうこと)を避けている。マクロ展開時の変数の捕獲を許容するように強制することもできるが、それは明示的に行わなければならない。また、Clojureでは、現在の名前空間にインポートされた別の名前空間の大域名を変更することは許容されていない。 言語の特徴
例Hello WorldCUI版のHello World: (println "Hello, world!")
GUI版の Hello World: (javax.swing.JOptionPane/showMessageDialog nil "Hello World")
スレッドセーフ重複しない通し番号の、スレッドセーフな生成器 (let [i (atom 0)]
(defn generate-unique-id
"Returns a distinct numeric ID for each call."
[]
(swap! i inc)))
無名クラス、マクロダミーの (def bit-bucket-writer
(proxy [java.io.Writer] []
(write [buf] nil)
(close [] nil)
(flush [] nil)))
(defmacro noprint
"Evaluates the given expressions with all printing to *out* silenced."
[& forms]
`(binding [*out* bit-bucket-writer]
~@forms))
(noprint
(println "Hello, nobody!"))
ソフトウェアトランザクショナルメモリそれぞれに10個の値の入った長さ100の配列(vector)があり、10×100=1,000個の異なる連続した数が格納されている。これら100個のvectorを16個のスレッドで共有する。各スレッドは、ランダムに2つのvectorを選びそれぞれの配列でランダムな位置を決めて、二つの配列の要素の内容を交換する、という作業をClojureのソフトウェアトランザクショナルメモリを使って行う。この作業を300,000回繰り返しても、最初に格納されていた1,000個の数はどれひとつとして失われることがない。 (defn run [nvecs nitems nthreads niters]
(let [vec-refs (vec (map (comp ref vec)
(partition nitems (range (* nvecs nitems)))))
swap #(let [v1 (rand-int nvecs)
v2 (rand-int nvecs)
i1 (rand-int nitems)
i2 (rand-int nitems)]
(dosync
(let [temp (nth @(vec-refs v1) i1)]
(alter (vec-refs v1) assoc i1 (nth @(vec-refs v2) i2))
(alter (vec-refs v2) assoc i2 temp))))
report #(do
(prn (map deref vec-refs))
(println "Distinct:"
(count (distinct (apply concat (map deref vec-refs))))))]
(report)
(dorun (apply pcalls (repeat nthreads #(dotimes [_ niters] (swap)))))
(report)))
(run 100 10 16 300000)
脚注
文献
外部リンク |