Clojure プログラミング言語

Eclipse の Clojure プラグインを利用する

Lisp はその表現力と能力で有名なプログラミング言語ですが、一般に、汎用目的で使うにはそれほど適していないと考えられていました。この考えを一変させたのが、Java™ プラットフォームで動作する Lisp の方言、Clojure です。今では Java 仮想マシンを使える場所であればどこでも Lisp の威力を利用することができます。この記事では Clojure を使い始める方法を説明し、Eclipse の Clojure プラグインを利用しながら Clojure の構文を学びます。

Michael Galpin, Software architect, eBay

Michael_GalpinMichael Galpin は eBay のアーキテクトであり、developerWorks に頻繁に寄稿しています。彼は JavaOne、EclipseCon、AjaxWorld など、さまざまな技術カンファレンスで講演を行っています。彼が現在取り組んでいることを知るには、Twitter で @michaelg をフォローしてください。



2009年 9月 22日

この記事では、Clojure プログラミング言語について説明します。Clojure は Lisp 方言の 1 つです。読者には Lisp についての知識がないという前提で話を進めますが、Java 技術については理解していることを前提とします。Clojure プログラムを作成するには、Java Development Kit V5 以降および Clojure ライブラリーが必要です。この記事では JDK V1.6.0_13 と Clojure V1 を使用しました。また、Eclipse の Clojure プラグイン (clojure-dev) を利用することになるので、Eclipse も必要になります。この記事で使用したのは、Eclipse V3.5 と clojure-dev 0.0.34 です。リンクについては、「参考文献」を参照してください。

Clojure とは何か

Java 仮想マシン (JVM) 上でプログラムを実行するということが、プログラムを Java プログラミング言語で作成することを意味していた時代は、それほど昔のことではありませんが、多くの選択肢からプログラミング言語を選べる現在では、その時代はもはや遠い過去の話となっています。Groovy、Ruby (JRuby を使用)、Python (Jython を使用) など、よく使用されているさまざまな言語から選択して、手続き型のスクリプト・プログラミングを行うことも、それぞれのプログラミング言語に特有のオブジェクト指向プログラミングを行うこともできます。スクリプト・プログラミングにしても、オブジェクト指向プログラミングにしても、Java プログラマーにはお馴染みのパラダイムです。これらの言語では、Java 言語で作成する場合と同じようなプログラムを作成すると言えます。ただ使用する構文が異なるだけにすぎません。

Clojure も JVM で動作するプログラミング言語の 1 つですが、Java 技術や前述の JVM 上で実行可能な他の言語とはかなり異なります。Clojure は Lisp の方言です。Lisp 系のプログラミング言語は長年使用されていて、その歴史は 1950年代にまで遡ります。Lisp は独特な S 式、あるいは前置記法を使用します。この表記は要するに (function arguments...) という形を取ります。つまり Lisp では、必ず関数の名前を先頭に記述し、関数に引数がある場合はその後に引数を続けることになります。関数とその引数は、括弧で囲んで 1 つにまとめられます。そのため、大量の括弧が Lisp のトレードマークの 1 つとなっています。

お察しのとおり、Clojure は関数型プログラミング言語です。関数型プログラミング言語としての Clojure の「純粋さ」については学者の議論の対象となり得ますが、mutable な状態の回避、再帰、高階関数などの関数型プログラミングの原理が Clojure で採用されていることに関しては議論の余地はありません。Clojure も動的に型付けされる言語ですが、コード内の重要なパスに対するパフォーマンスを改善するために、オプションで型情報を追加することもできます。Clojure は JVM 上で動作するだけでなく、Java との相互運用性も考慮して設計されています。最後に述べておく点として、Clojure は並行性を念頭に設計された言語であるため、並行プログラミングに関連する特有の機能がいくつか備わっています。


Clojure の実例

多くの人にとって新しい言語を学ぶのに最適な方法は、コードを実際に作成し始めることです。この考えのもと、これからいくつかの単純なプログラミング問題を例に、Clojure を使用してそれらの問題を解いていきます。Clojure はどのような動作をし、どのような使い方ができ、どのようなことを行うのに適しているのかを十分理解できるよう、Clojure で問題を解決する方法を詳しく説明しますが、まずは他のどの言語とも同じく、Clojure を操作するための開発環境をセットアップする必要があります。幸い、セットアップは Clojure では至って簡単な作業です。

最小限のセットアップ

Clojure を操作するために必要なのは、JDK と Clojure ライブラリー (1 つの JAR ファイル) だけです。一般に、Clojure プログラムを開発して実行する方法は 2 つありますが、最も一般的な方法は、Clojure の REPL (Read-Eval-Print-Loop) を使用することです。

リスト 1. Clojure の REPL
$ java -cp clojure-1.0.0.jar clojure.lang.Repl
Clojure 1.0.0-
user=>

上記のコマンドは、Clojure の JAR が置かれているディレクトリーから実行されています。JAR のパスは必要に応じて調整してください。また、スクリプトを作成して、そのスクリプトを実行するという方法も使えます。その場合には、clojure.main という名前の Java クラスを実行します。

リスト 2. Clojure main
$ java -cp clojure-1.0.0.jar clojure.main /some/path/to/Euler1.clj 
233168

ここでも同じく、パスは Clojure の JAR とスクリプトが配置されている場所に変更してください。最後に、Clojure には IDE サポートも用意されているため、Eclipseユーザーは Eclipse 更新サイトから clojure-dev プラグインをインストールすることができます。プラグインがインストールされた後、Clojure プロジェクトおよび Clojure ファイルを新規に作成するには、Java パースペクティブで操作してください (図 1 を参照)。

図 1. Eclipse の Clojure プラグイン、clojure-dev を使用する
Eclipse の Clojure プラグイン、clojure-dev を使用する

clojure-dev を使用すると、括弧の対応 (Lisp では必須) をはじめ、いくつかの基本構文が強調表示されます。さらに、Eclipse に直接組み込まれている REPL でスクリプトを起動することも可能です。この記事を執筆している時点では、このプラグインはまだ登場したばかりですが、その機能は急速に進化しています。基本的なセットアップはこれで完了しました。ここからは、Clojure プログラムを作成しながらこの言語の詳細を探っていきます。


例 1: シーケンスを処理する

Lisp という名前の由来は、「list processing (リスト処理)」です。Lisp ではあらゆるものがリストであるとよく言われますが、Clojure では、リストはシーケンスとして一般化されています。最初の例では、以下のプログラミング問題に取り組みます。

10 未満の自然数のうち、3 または 5 の倍数をすべてリストアップすると、3、5、6、9 となります。これらの倍数の和は 23 です。それでは、1,000 未満の 3 または 5 の倍数すべての和はいくつになるでしょうか。

この問題はProject Euler から引用しています。Project Euler は、巧みな (それほど巧みでない場合もありますが) コンピューター・プログラミングによって解く数学の問題集で、上記の問題はこの問題集の最初の問題です。リスト 3 に、Clojure を使用してこの問題を解く方法を記載します。

リスト 3. Project Euler から引用した例 1
(defn divisible-by-3-or-5? [num] (or (== (mod num 3) 0)(== (mod num 5) 0))) 

(println (reduce + (filter divisible-by-3-or-5? (range 1000))))

上記の最初の行では、関数を定義しています。Clojure では関数がプログラムの主要なビルディング・ブロックであることを覚えておいてください。ほとんどの Java プログラマーは、オブジェクトがプログラムのビルディング・ブロックであることに慣れているため、関数をビルディング・ブロックとして使用するのに慣れるまでには時間がかかるかもしれません。defn はこの言語のキーワードだと思うかもしれませんが、実はキーワードではなくマクロです。マクロを使用して Clojure コンパイラーを拡張することで、基本的に新しいキーワードを言語に追加することができます。したがって、defn は言語仕様の一部ではなく、言語のコア・ライブラリーによって追加されます。

上記で作成しているのは、divisible-by-3-or-5? という関数です。この名前は Clojure の命名規則に従っており、単語はハイフンで区切り、関数の名前の終わりに疑問符を付けて、これが true または false を返す述部であることを示しています。この関数は num というパラメーターしか取りませんが、複数の入力パラメーターがある場合には、大括弧のなかにスペースで区切って入れられます。

パラメーターの後に続くのは関数の本体です。上記では最初に or 関数を呼び出しています。通常使い慣れているのは論理 or 演算子ですが、上記の場合は単なる関数であって、演算子ではありません。この関数には 2 つのパラメーターを渡していますが、いずれのパラメーターも同じく式になっています。最初の式は、== 関数で始まっています。この関数は渡されたパラメーターを、値を基準に比較します。== 関数に渡されるパラメーターは 2 つありますが、その最初のパラメーターも式になっています。この式が呼び出す mod 関数は数学のモジュロ演算子であり、Java 言語での % 演算子に相当します。モジュロ演算子は除算の余りを返します。この場合で言うと、num を 3 で割ったときの余りです。その余りが 0 と比較されます (余りが 0 の場合は、num は 3 で割り切れるということです)。同じように、num を 5 で割った場合の余りが 0 であるかどうかをチェックしています。いずれかの余りが 0 であれば、関数が true を返します。

次の行では、式を作成して、その式を出力します。まずは最も内側にある括弧のなかから見ていきましょう。この括弧内では range 関数を呼び出して、数値 1,000 を渡しています。これによって、0 から始まり、1,000 未満のすべての数字による数列が作成されます。これが、3 または 5 で割り切れるかどうかをチェックする数字のセットそのものです。この括弧の外側では、filter 関数を呼び出しています。この関数は 2 つのパラメーターを取ります。最初のパラメーターは true または false を返す別の関数で、述部でなければなりません。2 番目のパラメーターは数列で、この例では (0, 1, 2, ... 999) という数列です。filter 関数は述部を適用し、述部が true を返した場合には数列の要素を結果に追加します。これに該当する述部は、上の行で定義された divisible-by-3-or-5? 関数です。

このフィルター式の結果は、3 または 5 で割り切れる 1,000 未満の整数からなる数列になります。これはまさに対象とする整数のセットなので、あとはこれらの整数の和をとればよいだけです。それには reduce 関数を使用します。この reduce 関数はパラメーターとして関数と数列の 2 つを取り、関数を数列の最初の 2 つの要素に適用し、その結果と数列の次の要素に再び関数を適用します。この例で該当する関数は + 関数、つまり加算です。したがって、数列のすべての要素の和が計算されることになります。

リスト 3 を見ると、このわずかなコードで多くのことが行われています。これが、Clojure の魅力の 1 つです。実行する内容は豊富ですが、いったんこの表記に慣れてしまえば、コードの内容は一目瞭然となります。同じことを Java コードで行うとしたら、遙かに大きく膨らんだコードになるはずです。今度は、別の例を見てみましょう。


例 2: 怠惰は美徳です

この例では、Clojure での再帰と遅延に目を向けます。これも、多くの Java プログラマーにとっては新しい概念です。Clojure では「怠惰な」数列、つまり必要になるまで要素が計算されない数列を定義することができます。そのため、Java 言語では決して目にすることのない無限数列を定義することが可能です。この機能がとりわけ役立つ例として、ここでは関数型プログラミングの別の重要な側面が関与する例を取り上げます。その重要な側面とは、再帰です。今回使用するのも Project Euler から引用したプログラミング問題ですが、今度は 2 番目の問題です。

フィボナッチ数列の新しい項は、前の 2 つの項の和によって生成されます。1 と 2 から始めると、最初の 10 個の項は 1、2、3、5、8、13、21、34、55、89 となるといった具合です。

この問題では、数列の項のうち、400 万未満の偶数の項をすべて合計した値を求めます。Java プログラマーはこの問題を解くために、n 番目のフィボナッチ数を求める関数を定義しようとするかもしれません。この関数を素直に実装すると、以下のようになります。

Listing 4. A naive Fibonacci function
(defn fib [n] 
    (if (= n 0) 0
        (if (= n 1) 1
            (+ (fib (- n 1)) (fib (- n 2))))))

上記では n が 0 であるかどうかをチェックし、0 であれば 0 を返します。次に n が 1 であるかどうかをチェックし、1 であれば 1 を返します。n が 0 でも 1 でもなければ、(n-1) 番目のフィボナッチ数と (n-2) 番目のフィボナッチ数を計算して、その答えを加算します。確かにこれで正しいのですが、Java プログラミングの経験が豊富であれば問題があることはわかるはずです。その問題とは、このような再帰的定義はスタックをすぐにいっぱいにするため、スタックがオーバーフローしてしまうことです。フィボナッチ数は無限数列を形作るため、Clojure の無限の遅延シーケンスを使用して、それ相応に記述しなければなりません。そのように記述したコードが、リスト 5 です。注意する点として、Clojure ではこれよりも効率的なフィボナッチ実装を標準ライブラリー (clojure-contrib) に用意していますが、その実装はさらに複雑になるため、ここでは Stuart Halloway の著書からフィボナッチ数列を引用しています (詳細は、「参考文献」を参照)。

リスト 5. フィボナッチ数の遅延シーケンス
(defn lazy-seq-fibo 
    ([] 
        (concat [0 1] (lazy-seq-fibo 0 1))) 
    ([a b] 
        (let [n (+ a b)] 
            (lazy-seq 
                (cons n (lazy-seq-fibo b n))))))

リスト 5 の lazy-seq-fibo 関数には 2 つの定義があります。最初の定義には引数がないため、空の大括弧となっています。2 番目の定義は 2 つの引数、[a b] を取ります。引数がない定義では、[0 1] のシーケンスを取り、これを式に連結します。連結先の式は lazy-seq-fibo の再帰呼び出しを行っていますが、その呼び出しでは 2 つの引数を取る定義を呼び出して 0 と 1 を渡しています。

2 つの引数を取る定義は let 式から始まっています。この式は Clojure での変数割り当てであり、[n (+ a b)] 式が変数 n を定義し、その値を a+b として設定した上で、次に lazy-seq マクロを使用しています。その名前からわかるように、lazy-seq マクロは遅延シーケンスを作成するために使用します。マクロの本体は式であり、上記では cons 関数を使用しています。Lisp で昔から使われているこの関数は、要素とシーケンスを取り込み、その要素をシーケンスの先頭に追加した新しいシーケンスを返します。この例で言う新しいシーケンスとは、再度 lazy-seq-fibo 関数を呼び出した結果です。このシーケンスが遅延シーケンスでなければ lazy-seq-fibo 関数は繰り返し呼び出されることになりますが、lazy-seq マクロのおかげで、この関数は要素が使用されるときにしか呼び出されません。このシーケンスの動作は、REPL を使用して確認することができます (リスト 6 を参照)。

リスト 6. フィボナッチ数を生成する
1:1 user=> (defn lazy-seq-fibo 
    ([] 
        (concat [0 1] (lazy-seq-fibo 0 1))) 
    ([a b] 
        (let [n (+ a b)] 
            (lazy-seq 
                (cons n (lazy-seq-fibo b n))))))
#'user/lazy-seq-fibo
1:8 user=> (take 10 (lazy-seq-fibo))
(0 1 1 2 3 5 8 13 21 34)

take 関数は、シーケンスから特定の数 (上記では 10) の要素を取得するために使用されています。これでフィボナッチ数を効果的に生成できるようになったので、いよいよ問題を解きにかかります。

Listing 7. Example 2
(defn less-than-four-million? [n] (< n 4000000))

(println (reduce + 
    (filter even? 
        (take-while less-than-four-million? (lazy-seq-fibo)))))

リスト 7 では、less-than-four-million? という関数を定義しています。この関数は単に、入力が 400 万未満であるかどうかのチェックをするだけです。次の式では、(わかりやすいように一番内側の式から説明すると) 一番内側の式で無限のフィボナッチ数列を取得します。その外側では take-while 関数を使用します。この関数は take 関数と似ていますが、take-while 関数が取るのは述部です。述部が false を返すと、数列からの取得を停止します。この例の場合、400 万より大きいフィボナッチ数を取得した時点で取得を停止し、その結果にフィルターを適用します。フィルターが使用する組み込み even? 関数は、ご想像のとおり数字が偶数であるかどうかをテストする関数です。テストの結果、400 万未満、かつ偶数のフィボナッチ数すべてを取得することになります。そして最初の例で行ったように、reduce を使用してすべての数字を合計するというわけです。

リスト 7 は問題の答えを出すものの、完全に満足できるものではありません。リスト 7 では take-while 関数を使用するために、極めて単純な less-than-four-million? という関数を定義しましたが、実は、その必要はありません。Clojure にはクロージャーのサポートが備わっていることを考えれば、それは当然のことです。クロージャーのサポートによって、リスト 8 のようにコードを単純化することができます。


Clojure でのクロージャー

クロージャーは多くのプログラミング言語で共通して使用されていますが、Clojure などの関数型言語では特によく使用されています。クロージャーは第一級市民の関数であり、他の関数に引数として渡せるだけでなく、インライン関数または匿名関数としても定義することができます。リスト 8 は、リスト 7 をクロージャーによって単純化したバージョンです。

リスト 8. 単純化したソリューション
(println (reduce + 
    (filter even? 
        (take-while (fn [n] (< n 4000000)) (lazy-seq-fibo)))))

リスト 8 では、fn マクロを使用しました。これによって匿名関数を作成して返しています。述部の関数は大抵とても単純なので、クロージャーを使って定義したほうが有効です。つまり、Clojure ではさらに簡潔にクロージャーを定義できることになります。

リスト 9. 簡潔なクロージャー
(println (reduce + 
    (filter even? 
        (take-while #(< % 4000000) (lazy-seq-fibo)))))

上記では、fn マクロの代わりに # を使ってクロージャーを作成しています。また、関数に渡す最初のパラメーターには % 記号を使用しました。関数が複数のパラメーターを受け入れる場合には、これと同様に、最初のパラメーターには %1 を使用し、その後のパラメーターは %2%3 というようにすることができます。

これまで単純な 2 つの例だけで、Clojure の多くの機能を見てきましたが、Clojure にはJava 言語との密接な統合というもう 1 つの重要な側面もあります。ここからは、Clojure から Java を利用すると有用な例を見てみましょう。


例 3: Java 技術を使用する

Java プラットフォームにはさまざまな機能が提供されています。JVM の優れたパフォーマンスや、コア API の豊富さ、そして Java 言語で作成された多数のサード・パーティー・ライブラリーの豊富さなどは、いずれも作業を一から始める手間や時間を省く強力な助けとなります。Clojure は、このような概念を中心に作成された言語です。そのため Java メソッドの呼び出し、Java オブジェクトの作成、Java インターフェースの実装、そして Java クラスの継承はいずれも簡単に行うことができます。その実例として、これからもう 1 つ、Project Euler の問題を引用します。

リスト 10. Project Euler からの問題 8
Find the greatest product of five consecutive digits in the 1000-digit number.

73167176531330624919225119674426574742355349194934 
96983520312774506326239578318016984801869478851843 
85861560789112949495459501737958331952853208805511 
12540698747158523863050715693290963295227443043557 
66896648950445244523161731856403098711121722383113 
62229893423380308135336276614282806444486645238749 
30358907296290491560440772390713810515859307960866 
70172427121883998797908792274921901699720888093776 
65727333001053367881220235421809751254540594752243 
52584907711670556013604839586446706324415722155397 
53697817977846174064955149290862569321978468622482 
83972241375657056057490261407972968652414535100474 
82166370484403199890008895243450658541227588666881 
16427171479924442928230863465674813919123162824586 
17866458359124566529476545682848912883142607690042 
24219022671055626321111109370544217506941658960408 
07198403850962455444362981230987879927244284909188 
84580156166097919133875499200524063689912560717606 
05886116467109405077541002256983155200055935729725 
71636269561882670428252483600823257530420752963450

この問題で扱うのは、1,000 桁の数字です。Java 技術では BigInteger を使用すれば、1,000 桁の数字を数値として表すことができますが、計算を行う際には数字全体を一度に扱う必要はありません。計算するのは一度に 5 桁だけなので、ストリングとして扱うほうが簡単です。ただし計算を行うには、この 5 桁を整数として処理しなければなりません。幸い、Java 言語にはストリングと整数とを切り替える API があります。そこでまず始めに、上記の大きくて手に負えないテキストを処理します。

リスト 11. テキストを解析する
(def big-num-str 
    (str "73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450"))

上記では、Clojure の複数行のストリングに対するサポートを利用しています。最初に str 関数で複数行のストリング・リテラルを解析し、次に def マクロを使って big-num-str という名前の定数を定義していますが、この場合に最も有効な方法は、これを整数のシーケンスに変換することです。そのためのコードを、リスト 12 に記載します。

リスト 12. 数列を作成する
(def the-digits
    (map #(Integer. (str %)) 
        (filter #(Character/isDigit %) (seq big-num-str))))

ここでも同じく最も内側の式から説明していくと、seq 関数を使用して big-num-str をシーケンスに変換しています。ただしこのシーケンスは、ここで目的とするシーケンスどおりとは言えません。その確認は、以下のように REPL を利用して行います。

リスト 13. big-num-str シーケンスを確認する
user=> (seq big-num-str)
(\7 \3 \1 \6 \7 \1 \7 \6 \5 \3 \1 \3 \3 \0 \6 \2 \4 \9 \1 \9 \2 \2 \5 \1 \1 \9
 \6 \7 \4 \4 \2 \6 \5 \7 \4 \7 \4 \2 \3 \5 \5 \3 \4 \9 \1 \9 \4 \9 \3 \4
 \newline...

REPL は文字 (Java の char) を \c として示すので、\7 は char 7、\newline は char \n (改行) となります。これはテキストを直接解析して取得した内容ですが、改行を除去して整数に変換してからでないと、有効な計算を行うことはできません。この変換を行っているのが、リスト 11 です。このコードではフィルターを使用して改行を除去しています。ここでも、filter 関数に渡す述部関数には簡潔なクロージャーを使用していることに注目してください。このクロージャーが使用する Character/isDigit は、java.lang.Character の静的メソッド isDigit です。つまりこのフィルターは数字の char だけを許容し、改行文字を破棄します。

改行は除去できたので、続いて整数への変換を行います。リスト 12 の内側から外側に目を向けると、map 関数を使用していることがわかります。この関数は関数とシーケンスの 2 つのパラメーターを取り、新しいシーケンスを返します。このシーケンスの n 番目の要素は、元のシーケンスの n 番目の要素に関数を適用した結果です。この関数にも簡潔なクロージャー表記が使用されています。最初に Clojure の str 関数を使って文字をストリングに変換していますが、なぜこの変換を行うかと言えば、次に java.lang.Integer のコンストラクターを使用して整数を作成するからです。これは、Integer によって表されています。この式は、新しい java.lang.Integer(str(%)) であると考えることができます。この式を map 関数と一緒に使用することで、目的どおり、整数のシーケンスを取得しています。それではいよいよ、問題を解いてみましょう。

リスト 14. 例 3
(println (apply max 
    (map #(reduce * %)
        (for [idx (range (count the-digits))] 
            (take 5 (drop idx the-digits))))))

上記のコードを理解するには、まず for マクロに目を向けてください。これは、Java 言語での for ループのようなものではありません。この for マクロはシーケンスの内包で、上記ではまず、大括弧を使ってバインディングを作成します。この例では、変数 idx を 0 から N-1 までのシーケンスにバインディングします。ここで、N はシーケンス the-digits に含まれる要素の数です (元の数字は 1,000 桁であったため、N = 1,000)。次に、for マクロは新しいシーケンスを生成するために使用する式を取ります。そして idx シーケンスの各要素を繰り返し処理して式を評価し、その結果を戻りシーケンスに追加します。これはある意味、for ループのように機能することが見てとれます。内包のなかで使用される式は、まず drop 関数によってシーケンスの最初の M 要素を破棄してから、take 関数を使って短縮されたシーケンスの最初の 5 つの要素を取得します。M 要素は 0 から 1、1 から 2 のようになることを思い出してください。したがって、結果はシーケンスで構成されたシーケンスとなり、最初の要素は (e1, e2, e3, e4, e5)、次の要素は (e2, e3, e4, e5, e6) のようになります。ここで、e1、e2 などは the-digits の要素です。

シーケンスで構成されたシーケンスが用意できたところで、今度は map 関数の出番です。この関数のパラメーターである reduce 関数によって、各シーケンスをそのシーケンスに含まれる 5 つの数字の積に変換します。変換後の整数のシーケンスでは、最初の要素が要素 1 から 5 までの積、次の要素が要素 2 から 6 までの積というようになります。これらの積のなかで最大の値を求めるには、max 関数を使用します。ただし max 関数が引数として要求しているのは、単一のシーケンスではなく、複数の要素であるため、apply 関数を使用してシーケンスを複数の要素に変換してから max 関数に渡しています。これによって問題を解くために必要としていた最大の積の値が算出され、もちろんその答えが出力されます。以上で、いくつかの問題を解くと同時に、Clojure の使い方を学べたはずです。


まとめ

この記事では Clojure プログラミング言語を紹介し、Eclipse の Clojure プラグインを使用して、この言語が持つメリットを利用しました。Clojure の理念と機能のいくつかについては概説するにとどめた一方、サンプル・コードに重点を置き、単純な例を使ってこの言語のコア機能の多くを説明しました。具体的には、関数、マクロ、バインディング、再帰、遅延シーケンス、クロージャー、内包、そして Java 技術との統合について説明しましたが、これ以外にも Clojure には多くの側面があります。この記事を読んで、皆さんが Clojure に関心を持ったこと、そして参考文献を調べて Clojure についてさらに詳しく学ぶ気になったことを期待しています。


ダウンロード

内容ファイル名サイズ
Article source codeos-eclipse-clojure-euler.zip2KB

参考文献

学ぶために

製品や技術を入手するために

  • Java Development Kit V5 以降が必要です。この記事では Java Development Kit V1.6.0_13 を使用しています。
  • Clojure V1 をダウンロードしてください。
  • 最新の Eclipse IDE を入手してください。この記事では Eclipse V3.5 を使用しました。
  • clojure-dev は Eclipse プラットフォームでビルドされたClojure プログラミング言語対応の IDE です。この記事では V0.0.34 を使用しました。
  • BM ソフトウェアの試用版を使用して、次のオープンソース開発プロジェクトを革新してください。ダウンロード、あるいは DVD で入手できます。
  • IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=438835
ArticleTitle=Clojure プログラミング言語
publish-date=09222009