Clojure+Hadoop(Cloud) = Cloudure(第一歩)

前回のClojureでHadoopのサンプルのうち、定型処理っぽい部分をマクロにしてみました。
プロジェクト名はClojure + Cloud = Cloudureです。
名前負けしてます。まだいまいち。(ClojureもHadoopもそんな使った事ない。。)

2010/03/07 Clojarsにデプロイしました(namespaceが変えました)

マクロの定義はいまのとここんな感じ。使い方は

;; Mapper、Reducerを別関数にする場合
(defn hello-map [key value context]
  (.write context (Text. key) (IntWritable. (Integet/parseInt value)))
  )
(defn hello-reduce [key values context]
  (.write context key (IntWritable. (reduce + (map #(.get %) values))))
  )
(defmapreduce hello
  :mapper hello-map
  :reducer hello-reduce
  )
;; Mapper、Reducerもいっしょに定義する場合
(defmapreduce hello
    :mapper ([key value context] (.write context (Text. key) (IntWritable. (Integet/parseInt value))))
    :reducer ([key values context] (.write context key (IntWritable. (reduce + (map #(.get %) values)))))
    )

前者を展開した場合、

(do
 (gen-class
  :name
  org.clojars.making.cloudure.core.hello.mapper
  :extends
  org.apache.hadoop.mapreduce.Mapper
  :prefix
  "hello-mapper-")
 (defn hello-mapper-map [G__2310 G__2311 G__2312 G__2313]
   (hello-map G__2311 G__2312 G__2313))
  (gen-class
   :name
   org.clojars.making.cloudure.core.hello.reducer
   :extends
   org.apache.hadoop.mapreduce.Reducer
   :prefix
   "hello-reducer-")
  (defn hello-reducer-reduce [G__2314 G__2315 G__2316 G__2317]
    (hello-reduce G__2315 G__2316 G__2317))
  (defn get-hello-job
      ([] (get-hello-job true))
    ([set-jar?]
     (let [job (org.apache.hadoop.mapreduce.Job.)]
       (if set-jar?
           (.setJarByClass job (forName "org.clojars.making.cloudure.core")))
       (doto
        job
        (.setMapperClass
         (forName "org.clojars.making.cloudure.core.hello.mapper"))
        (.setReducerClass
         (forName "org.clojars.making.cloudure.core.hello.reducer")))))
    {:tag java.lang.Class}))

後者は

(do
 (gen-class
  :name
  org.clojars.making.cloudure.core.hello.mapper
  :extends
  org.apache.hadoop.mapreduce.Mapper
  :prefix
  "hello-mapper-")
 (defn hello-mapper-map [this key value context]
   (.write context (Text. key) (IntWritable. (parseInt value))))
  (gen-class
   :name
   org.clojars.making.cloudure.core.hello.reducer
   :extends
   org.apache.hadoop.mapreduce.Reducer
   :prefix
   "hello-reducer-")
  (defn hello-reducer-reduce [this key values context]
    (.write context key (IntWritable. (reduce + (map #(.get %) values)))))
  (defn get-hello-job
      ([] (get-hello-job true))
    ([set-jar?]
     (let [job (org.apache.hadoop.mapreduce.Job.)]
       (if set-jar?
           (.setJarByClass job (forName "org.clojars.making.cloudure.core")))
       (doto
        job
        (.setMapperClass
         (forName "org.clojars.making.cloudure.core.hello.mapper"))
        (.setReducerClass
         (forName "org.clojars.making.cloudure.core.hello.reducer")))))
    {:tag java.lang.Class}))

前回のサンプルのdefmapreduce版はこんな感じ。

まだまだですね。もっとHadoopを使って慣れながら改良していきたいと思います。

一応Cloudureを使うには、Clojarsに上げてあるので、project.cljに↓を追加すればOK

[org.clojars.making/cloudure "0.1.0-SNAPSHOT"]

コメント・アドバイスがあれば@makingまで。

Created at : 2010-03-01 02:34:55   Updated at : 2010-03-10 03:01:31
Category : Programming::Lisp::Clojure

CassandraをWindowsで使う

LinuxやMacOSXの場合はREADME読んで。

インストール

バイナリが用意されているので適当な場所にDLして展開。
展開してできたフォルダのパスをCASSANDRA_HOMEとして環境変数に設定する。

サーバー起動

%CASSANDRA_HOME%\bin\cassandra.batをダブルクリック。あら簡単。

ログやデータはデフォルトで%CASSANDRA_HOME%\lib\var以下にできる模様。(なんか変なドライブまでできとる。。。)

クライアント(CLI)接続

%CASSANDRA_HOME%\bin\cassandra-cli.batをダブルクリック。

connectを実行した後はREADME通り。

cassandra> connect localhost/9160
Connected to localhost/9160
cassandra> set Keyspace1.Standard1['jsmith']['first'] = 'John'
Value inserted.
cassandra> set Keyspace1.Standard1['jsmith']['last'] = 'Smith'
Value inserted.
cassandra> set Keyspace1.Standard1['jsmith']['age'] = '42'
Value inserted.
cassandra> get Keyspace1.Standard1['jsmith']
=> (column=last, value=Smith, timestamp=1267619133099)
=> (column=first, value=John, timestamp=1267619123162)
=> (column=age, value=42, timestamp=1267619142661)
Returned 3 results.

Created at : 2010-03-03 21:26:36   Updated at : 2010-03-03 22:32:06
Category : Middleware::NoSQL::Cassandra

Clojureのdefmacroで強制的にシンボル捕捉させる

Clojureはマクロ定義内のシンボルを名前空間で修飾する。シンボル束縛を回避するためであり、バグを防いでくれるが、次のような書き方をしたいときはNG。

(defmacro foo [x]
  `(defn ~x [x]
     x
     )
  )
user> (macroexpand-1 '(foo aaa))
(clojure.core/defn aaa [user/x] user/x)                                                                                              
user> (foo aaa)

Can't use qualified name as parameter: user/x
  [Thrown class java.lang.Exception]

シンボル束縛を強制的に行うためには~'をつける。

(defmacro foo [x]
  `(defn ~x [~'x]
     ~'x
     )
  )
user> (macroexpand-1 '(foo aaa))
(clojure.core/defn aaa [x] x)                                                                                                        
user> (foo aaa)
#'user/aaa                                                                                                                           
user> (aaa 100)
100

使うときは要注意。
尤も、このケースでは(gensym)を使うべきなのだが。。。

→ (追記:2010/03/03)Clojureはマクロ内で使うシンボルの末尾に「#」をつければ勝手にgensymしてくれます。

user> (defmacro foo [x] `(defn ~x [x#] x#))
#'user/foo
user> (macroexpand-1 '(foo aaa))
(clojure.core/defn aaa [x__4956__auto__] x__4956__auto__)

Created at : 2010-02-26 03:22:45   Updated at : 2010-03-03 12:23:14
Category : Programming::Lisp::Clojure