Programming

clojure-maven-pluginを使ってMavenでClojure+Javaビルド

これまでさんざんClojureのビルドツールとしてLeiningenを推してきましたが。Leiningenの気に入らない点として

  • Javaをビルドできない(プラグイン作ればすぐ出来そうな気もするけど)
  • 毎回jarがlibディレクトリにコピーされる

を感じていました。
mavenなら↑のようなことはないので、mavenでClojureがコンパイルできてrepl起動して、swank-server立ち上げられるといいなと思っていたら、とっくにありますね、clojure-maven-plugin
このプラグインを使ってすぐにMavenでClojureを使えるようにblankプロジェクトを作ってGithubにコミットしました。 maven-clojure-blank

blank project取得

$ git clone git://github.com/making/maven-clojure-blank.git

.gitディレクトリを削除して、project名(ディレクトリ名、pom.xmlの設定)を変えて使ってください

コンパイル

mvn compileでClojureコードもJavaコードもコンパイルできます。ソースフォルダに

src/main/clojure/am/ik/blank/hello.clj
src/main/java/am/ik/blank/Hello.java

があったとき、

$ mvn compile
$ find target/classes
target/classes
target/classes/am
target/classes/am/ik
target/classes/am/ik/blank
target/classes/am/ik/blank/hello$foo__3.class
target/classes/am/ik/blank/hello$loading__6309__auto____1.class
target/classes/am/ik/blank/Hello.class
target/classes/am/ik/blank/hello__init.class

と両方classファイルできてますね。OK。
Clojureのコンパイルだけしたい場合はmvn clojure:compile

テスト

src/test/clojureにテストコード書いておけばmvn testでjavaのテストと一緒にclojureのテストも行われます。
Clojureのテストだけしたい場合はmvn clojure:test

REPL

replもできます。特徴はクラスパスでしょう。

$ mvn clojure:repl
Clojure 1.1.0
user=> (use 'clojure.contrib.classpath)
nil
user=> (doseq [cp (classpath)] (println cp))
#<File /Users/maki/work/maven-clojure-blank/target/classes/../generated-sources>                                              
#<File /Users/maki/work/maven-clojure-blank/src/main/clojure>                                                                 
#<File /Users/maki/work/maven-clojure-blank/src/test/java>                                                                    
#<File /Users/maki/work/maven-clojure-blank/src/test/clojure>                                                                 
#<File /Users/maki/work/maven-clojure-blank/target/classes>                                                                   
#<File >                                                                                                                      
#<File /Users/maki/work/maven-clojure-blank/target/classes>                                                                   
#<File /Users/maki/.m2/repository/org/clojure/clojure/1.1.0/clojure-1.1.0.jar>                                                
#<File /Users/maki/.m2/repository/org/clojure/clojure-contrib/1.1.0/clojure-contrib-1.1.0.jar>                                
#<File /Users/maki/.m2/repository/jline/jline/0.9.94/jline-0.9.94.jar>                                                        
#<File /Users/maki/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar>                                                          
#<File /Users/maki/.m2/repository/swank-clojure/swank-clojure/1.1.0/swank-clojure-1.1.0.jar>   
nil

clojureのソースディレクトリ、ビルド出力ディレクトリ、pom.xmlで指定した依存ライブラリが含まれています。これでJavaをコンパイルしてもすぐにreplで使えますね。

swank

Lisp開発と言えばやっぱりSlimeを使いたいですよね。mavenでswank-serverを立ち上げることでrepl同様のクラスパス込の状態で使えます。mvn clojure:swankでswank立ち上げたあとemacsでM-x slime-connectでOK。

スクリプト実行

以下のようにconfigurationscriptに実行したいclojureスクリプトのパスを書いておけばmvn clojure:runでロードされます。もちろんクラスパスは同上。

<plugin>                                                                                                                      
  <groupId>com.theoryinpractise</groupId>                                                                               
  <artifactId>clojure-maven-plugin</artifactId>                                                                         
  <version>1.3.1</version>                                                                                              
  <configuration>                                                                                                             
    <script>src/main/script/run.clj</script>                                                                            
  </configuration>                                                                                                            
  ...
</plugin>

これだけ書くとleiningenいらないじゃんって思いそうですが、 mavenは立ち上がりが遅いのでちょっとイラッとするかもです。(Leinigenも結構遅いですがw)
Javaでライブラリを開発してClojureで呼び出すっていう使い方をしたい場合はこの方式が良さそうです。 ClojureのみのプロジェクトであればLeiningenで十分かなと思います。


Mavenについては
Maven: The Definitive Guide
が詳しいです。
実はここからPDFでまるごとDLできますw

Created at : 2010-03-14 01:50:58   Updated at : 2010-03-14 02:48:18
Category : Programming::Lisp::Clojure

拡張FizzBuzz (CommonLisp版)

ここからの編集して転載。

FizzBuzzの拡張で、3、5以外にも増えたらどうする?

拡張FizzBuzz定義マクロ

(defmacro define-fizzbuzz (fizz-buzz-list)
  `(defun ,(read-from-string (format nil "~{~a~^-~}" (mapcar #'cdr fizz-buzz-list))) (x)
     (format nil "~{~a~^~}"
             (nconc ,@(loop for i in fizz-buzz-list collect `(if (zerop (mod x ,(car i))) (list ',(cdr i))))))
     )
  )
;; 使い方
(define-fizzbuzz ((3 . fizz) (5 . buzz))) ; fizz-buzz
(define-fizzbuzz ((3 . fizz) (5 . buzz) (7 . foo))) ; fizz-buzz-foo
(define-fizzbuzz ((3 . fizz) (5 . buzz) (7 . foo) (11 . bar))) ; fizz-buzz-foo-bar

展開すると、

CL-USER> (macroexpand-1 '(define-fizzbuzz ((3 . fizz) (5 . buzz))))
(DEFUN FIZZ-BUZZ (X)
  (FORMAT NIL "~{~a~^~}"
          (NCONC (IF (ZEROP (MOD X 3)) (LIST 'FIZZ))
                 (IF (ZEROP (MOD X 5)) (LIST 'BUZZ)))))
T
CL-USER> (macroexpand-1 '(define-fizzbuzz ((3 . fizz) (5 . buzz) (7 . foo))))
(DEFUN FIZZ-BUZZ-FOO (X)
  (FORMAT NIL "~{~a~^~}"
          (NCONC (IF (ZEROP (MOD X 3)) (LIST 'FIZZ))
                 (IF (ZEROP (MOD X 5)) (LIST 'BUZZ))
                 (IF (ZEROP (MOD X 7)) (LIST 'FOO)))))
T
CL-USER> (macroexpand-1 '(define-fizzbuzz ((3 . fizz) (5 . buzz) (7 . foo) (11 . bar))))
(DEFUN FIZZ-BUZZ-FOO-BAR (X)
  (FORMAT NIL "~{~a~^~}"
          (NCONC (IF (ZEROP (MOD X 3)) (LIST 'FIZZ))
                 (IF (ZEROP (MOD X 5)) (LIST 'BUZZ))
                 (IF (ZEROP (MOD X 7)) (LIST 'FOO))
                 (IF (ZEROP (MOD X 11)) (LIST 'BAR)))))
T

一応、実行結果(fizz-buzz-fooのみ)。

CL-USER> (loop for i from 1 to 105 do (format t "~a -> ~a~%" i (fizz-buzz-foo i)))
1 -> 1
2 -> 2
3 -> FIZZ
4 -> 4
5 -> BUZZ
6 -> FIZZ
7 -> FOO
8 -> 8
9 -> FIZZ
10 -> BUZZ
11 -> 11
12 -> FIZZ
13 -> 13
14 -> FOO
15 -> FIZZBUZZ
16 -> 16
17 -> 17
18 -> FIZZ
19 -> 19
20 -> BUZZ
21 -> FIZZFOO
22 -> 22
23 -> 23
24 -> FIZZ
25 -> BUZZ
26 -> 26
27 -> FIZZ
28 -> FOO
29 -> 29
30 -> FIZZBUZZ
31 -> 31
32 -> 32
33 -> FIZZ
34 -> 34
35 -> BUZZFOO
36 -> FIZZ
37 -> 37
38 -> 38
39 -> FIZZ
40 -> BUZZ
41 -> 41
42 -> FIZZFOO
43 -> 43
44 -> 44
45 -> FIZZBUZZ
46 -> 46
47 -> 47
48 -> FIZZ
49 -> FOO
50 -> BUZZ
51 -> FIZZ
52 -> 52
53 -> 53
54 -> FIZZ
55 -> BUZZ
56 -> FOO
57 -> FIZZ
58 -> 58
59 -> 59
60 -> FIZZBUZZ
61 -> 61
62 -> 62
63 -> FIZZFOO
64 -> 64
65 -> BUZZ
66 -> FIZZ
67 -> 67
68 -> 68
69 -> FIZZ
70 -> BUZZFOO
71 -> 71
72 -> FIZZ
73 -> 73
74 -> 74
75 -> FIZZBUZZ
76 -> 76
77 -> FOO
78 -> FIZZ
79 -> 79
80 -> BUZZ
81 -> FIZZ
82 -> 82
83 -> 83
84 -> FIZZFOO
85 -> BUZZ
86 -> 86
87 -> FIZZ
88 -> 88
89 -> 89
90 -> FIZZBUZZ
91 -> FOO
92 -> 92
93 -> FIZZ
94 -> 94
95 -> BUZZ
96 -> FIZZ
97 -> 97
98 -> FOO
99 -> FIZZ
100 -> BUZZ
101 -> 101
102 -> FIZZ
103 -> 103
104 -> 104
105 -> FIZZBUZZFOO

他の言語ならどう書く?

Created at : 2010-02-25 02:37:43   Updated at : 2010-02-26 08:33:18
Category : Programming::Lisp::CommonLisp

REPLでクラスパス表示

すぐわすれるのでメモ

=> (seq (.getURLs (ClassLoader/getSystemClassLoader)))
(#<URL file:/Users/maki/work/> #<URL file:/opt/local/share/java/clojure/lib/clojure.jar>)

Created at : 2010-02-20 03:27:05   Updated at : 2010-02-20 03:27:45
Category : Programming::Lisp::Clojure