Kuchitama Tech Note

はてな記法がいつまでたっても覚えられないので、はてなダイアリーからマークダウンが使えるこっちに引っ越してきました。

Clojure+LeiningenでJavaFXプログラミング

JavaFX GUIプログラミング〈Vol.1〉をちょいちょい読み始めたので、 JavaFXアプリをClojure + Leiningenで開発する方法についてまとめる。

JavaFX GUIプログラミング〈Vol.1〉

JavaFX GUIプログラミング〈Vol.1〉

プロジェクトの作成

なにはともあれプロジェクトが無くては始まらないのでプロジェクトを作成

lein new javafx

で、とりあえずJavaFXを実行するにはjfxrt.jarが必要。 jfxrt.jarはMacOSXだと /Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home/jre/lib/jfxrt.jar にあるっぽいので、そこからコピって来た。 ここのディレクトリにパスが通っていれば問題ないと思うのだけど、デフォでは通ってないっぽい。 作成したプロジェクトディレクトリ直下にlibディレクトリを作成して、その中にjfxrt.jarをコピー。

ただ、手動でjarを追加すると、基本的にLeiningenさんはjarを認識してくれないので、project.cljにjarのパスを追加してやる必要がある。 パスを通すには、:resource-pathsというプロパティを追加してやる。

これでコンパイル時のクラスパスにlib/jfxrt.jarが追加される。 ついでに、:mainを追加して、lein runしたときにちゃんと実行されるように設定しておく。

JavaFX Applicationの作成

準備が完了したので、早速プログラムの作成に入る。 Leiningenのデフォルトだと、src/javafx/core.cljが作成されているのだけど、今回は、自分でsrc/kuchitama/javafx.cljを作成し、そこに実装した。 わざわざ自分でパッケージを切った経緯については、@t_nodaさんと@bouzuyaさんのやり取りを元にしている。詳しくはこちらを参照

で、肝心のプログラムだけどこんな感じ

これで、JavaFX GUIプログラミングのP.8あたりのサンプル相当の実装になる。 こいつをベースにいじって行けば、Clojureでいろいろ出来るハズ

課題

このサンプルだと、lein uberjarを実行したときに、生成されるスタンドアローンなjarの中にjfxrt.jarが含まれないので、実はスタンドアローンで動作しない。 これを解決するには、そもそも手動でのライブラリ追加をやめてちゃんとLeiningenさんに依存関係を解決してもらえばいいのだけど、oracleはjfxrt.jarをmavenで公開していないため、それは不可能になっている。 その対応として自分でローカルのmavenリポジトリを作成して、そこにjfxrt.jarを追加するという方法がある。(参考) 確かに、これが一番オーソドックスな方法だと思うのだけど、僕はわざわざjfxrt.jarだけを管理するためにローカル環境にmavenリポジトリを作りたくはなかったので、今回手動追加という方法をとった。 でも、スタンドアローンなjarがスタンドアローンに動作しないのは詐欺もいいところなのでなんとかしたい。 どなたか、方法をご存知の方がいらっしゃればご教授ください。

ゆるふわなClojure勉強会をやってみた-変態アドベントカレンダー(17 あるいは 21かもしれない 日目)

この記事は変態アドベントカレンダー in Summerの (えっと多分)17日目とい(というは、実はスタートから21日目)の記事です。

前回は、@dproject21さんのTitanium mobile の Alloyでエロい(?)アプリをつくる。でした。

今日のお題

今日は、先日主催させていただいたClojureProgrammingお菓子会のレポート記事を書きたいと思います。

え?ジャッジメントはどこに行ったか?
実は飽き…いや、Clojureお菓子会が予想以上に変態的な催しになったので、これは報告せねばだと思ったので予定変更してお送りしております。

What is ClojureProgrammingお菓子会

ClojureProgrammingという本がオライリーから出版されており、前回のKyoto.cljで、これが良書だと紹介して頂きました。
そこで早速会社でも買ってもらったのですが、これが英語なんですね。
しかも、分厚いんですね。
なので、読むの大変なんです。
そこで、まぁ、一人だと心が折れそうなので仲間を集めたくて読書会を開催しました
ただ、なるべくゆったりやりたいので、お菓子会っていうゆるふわな名前にしました。
内容は、単にお菓子食いながら本を読もうってだけです。

なぜ、この会が変態的なのか

それは…

真夏

男ども

汗だく

ハァハァとしていた

異様な勉強会だったからです。

まぁ、それは会場を用意した僕が、空調にまで気が回らず、暑い中ノークーラーで半日過ごすはめになっただけですが…

しかし、内容は思ったよりも濃い内容になりました。

僕は、13章Testについて発表
当日までに資料が準備出来ず、不格好なライブコーディングを行うという、なんともゆるい発表になりましたが、
その他にも、Clojrueのベースになる知識やREPLについてのお話がありました。

ClojureProgramming / Chap.13 Testing

僕は、先述の通りTestingについて話しました。
とはいえ、前半のところだけしかまとまってませんが・・・
どうもTestの章はサンプルコードが少ないように感じたので、
ちょっと自分なりに考えたサンプルをスライドに まとめているので、
ClojureProgrammingを読まれる方は、一緒に見て頂くとちょっと読解のお役に立てるかもしれません。

↓スライドはこちら

まとめ

  1. 連載形式など無かった
  2. ClojureProgrammingは良書ですよ
  3. 英語に疲れたらお菓子を食べよう
  4. またやるので興味ある人は次は来てね
  5. kyoto.cljはどこに行ったのか…

ジャッジメントですの変換を作ってみる

この記事は変態アドベントカレンダー in Summer 9日目の記事です。

昨日の記事は@tango238さんの、

java.lang.Stringのメソッドをむりやり書き換える」でした。

今回のおだい

ただの悪ノリだけで始まった変態アドベントカレンダーinSummerですが、
なぜか参加者が2桁になってしまったので、僕も書きます。

今回は、確実に3週はしそうなので、連載形式で書いてみようと思います。
ゴールである夏コミまでお付き合いいただければ幸いです。

と、いうことでお題を「ジャッジメントですの 変換」の実装にしました。
これをパクる感じで、
入力された文字列の語尾を「ですの」に変換してtwitter投稿するアプリを作ってみようと思います。
atndの画像や注意文とも相まって良い感じです。

ちなみに、ありえないと思いますが、「ジャッジメントですの」って何ってかたは、この辺で勉強しとくといいと思います。

連載の予定

来週のサ○エさんは

とりあえずClojureでなっ

  • xmlを操作してみる。

ただしClojureでなっ

もちろんClojureでなっ

の、3本です。

Clojrue使っているのは、100%趣味です。

形態素解析してみる

とりあえず、今回は、適当な文章を形態素解析して助動詞を抜き出すところまでやろうと思います。

本題に入る前にちょっとだけ、用語について

ということで、最低限必要な知識はバッチリですね。

形態素解析というと、MeCabとかIgoとかあります。
ありますが今回は、関○電力のお達しにより、省エネモードで進めていこうと思います。

なので、今回はYahoo!日本語形態素解析APIを使おうと思います。

このAPIに、解析したい日本語文章を渡すと、解析結果のXMLが帰ってきます。
「この記事は変態アドベントカレンダー in Summer 9日目の記事です。」という文章を解析した結果を、このエントリの一番最後に載せておきます。

ClojureAPIをたたいてみる

さて、このAPIClojureから叩きたいのですが、
わざわざPureJavaを読み出すのも面白く無いので、今回は、
clj-httpというラッパーライブラリを使います。
https://github.com/dakrone/clj-http/

こいつは、Apache HttpComponentsをClojureでラップしていて、さくっとClojureからHttp-Get/Postのアクセスを行うことができます。
省エネだぜぇ

ということで、サンプルコード

(ns judgement.core
  (:require [clj-http.client :as client]))

(def yahoo-url
  ;yahoo形態素解析APIのURL
  "http://jlp.yahooapis.jp/MAService/V1/parse?appid={あなたのappId}&results=ma&response=surface,pos&sentence=")

(defn enc [in-str]
  ;;与えられた文字列をURLEncodeして返す
  (. java.net.URLEncoder encode in-str "utf8"))

(defn access-yahoo-api [src-str]
  ;;yahoo形態素解析APIにアクセスする
  (client/get 
    (str yahoo-url 
	  (enc src-str))))

はい、これだけです。
これで、Yahooの形態素解析APIを叩けます。
省エネだろぅ?

まとめ

というわけで、今日の記事のまとめはこんな感じ

明日は、@megascusさんの記事です。
よろしくお願いします。

おまけ

<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:jp:jlp" xsi:schemaLocation="urn:yahoo:jp:jlp http://jlp.yahooapis.jp/MAService/V1/parseResponse.xsd">
<ma_result>
<total_count>19</total_count>
<filtered_count>19</filtered_count>
<word_list>
<word>
<surface>この</surface>
<pos>連体詞</pos>
</word>
<word>
<surface>記事</surface>
<pos>名詞</pos>
</word>
<word>
<surface></surface>
<pos>助詞</pos>
</word>
<word>
<surface>変態</surface>
<pos>名詞</pos>
</word>
<word>
<surface>アド</surface>
<pos>名詞</pos>
</word>
<word>
<surface>ベント</surface>
<pos>名詞</pos>
</word>
<word>
<surface>カレンダー</surface>
<pos>名詞</pos>
</word>
<word>
<surface></surface>
<pos>特殊</pos>
</word>
<word>
<surface>in</surface>
<pos>名詞</pos>
</word>
<word>
<surface></surface>
<pos>特殊</pos>
</word>
<word>
<surface>Summer</surface>
<pos>名詞</pos>
</word>
<word>
<surface></surface>
<pos>特殊</pos>
</word>
<word>
<surface>9</surface>
<pos>名詞</pos>
</word>
<word>
<surface></surface>
<pos>接尾辞</pos>
</word>
<word>
<surface></surface>
<pos>名詞</pos>
</word>
<word>
<surface></surface>
<pos>助詞</pos>
</word>
<word>
<surface>記事</surface>
<pos>名詞</pos>
</word>
<word>
<surface>です</surface>
<pos>助動詞</pos>
</word>
<word>
<surface></surface>
<pos>特殊</pos>
</word>
</word_list>
</ma_result>
</ResultSet>

変態なWebで入力してみる

このエントリは変態アドベントカレンダー11/28のエントリです。

なんか、アドベントカレンダーが途切れそうなので、やっつけですが投稿します。

前々回に作った変態Webにフォームを付けてみました。
これで、POSTやGETの入力を取得して、動的にページに反映させることが出来ます。

ということで、さっそくindexのテンプレートはこんな感じ

index.html.fleet
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Input your name</title>
</head>
<body>
<form action="./hentai" method="POST">
名前を入力してください。<input type="text" name="user" /><br />
<input type="submit" />
</form>
</body>
</html>

ユーザに入力を促して、入力されるとページが遷移するとてもシンプルなページです。
一応fleetのテンプレートとして作りましたが、動的な処理は行っていないので、テンプレートの意味はありません。

で、これを受けて表示されるのが次のテンプレート

hentai.html.fleet
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>This is Hentai web</title>
</head>
<body>
<h1><(data :user-name)>さん。あなたは変態です</h1>
<img src="<(data :link)>" />
</body>
</html>

前回のとおり、fleetでは<( )>の中にclojureのコードを埋め込めるので、入力されたuser-nameと前回同様のリンクを取得して、htmlを生成します。

で、肝になるcompojureの処理はこちら

hentai/core.clj
(ns hentai.core
  (:use compojure.core compojure.response fleet
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))

(fleet-ns templates "templates" [:fleet :bypass])

(extend-protocol Renderable fleet.util.CljString 
    (render [this _] (-> (response (.toString this))
                       (content-type "text-html; charset=UTF-8"))))

(def hentai-link ["http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854674_photo0.jpg" "http://yipev.com/data/img2/20110408084405/thumb_e1r73nh7fv301cd1uweinzjw.jpg" "http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854662_photo0.jpg"])
(defn get-img []  (rand-nth hentai-link))

(defroutes main-routes
  (GET "/" _ (templates/index))
  (POST "/hentai" {params :params} (templates/hentai {:link (get-img) :user-name (params :user)}))
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

肝になるのは、この部分↓
(POST "/hentai" {params :params} (templates/hentai {:link (get-img) :user-name (params :user)}))

{params :params}のところで、POSTされた値を受け取ってます。
受け取ってしまえば、後は前回の画像をテンプレートに渡すのと一緒で、
{:link (get-img) :user-name (params :user)}
を、テンプレートに渡してやればOKです。

このMapを渡すあたりが、clojureっぽいですね。
と、これだけでちょっとインタラクティブな感じになりました。

Webページにあなたの名前が乗るだけで、罵られ度が増しました。

デモはこちら↓
http://kuchitama.ddo.jp

clojureで変態webシステムを作ってみた

このエントリは変態アドベントカレンダー11/15です

今日は変態のためのWebサイトを、clojureというJVM上で動く関数型言語を使って開発してみようと思います。

今回の開発にあたって、次のライブラリを用いています。
このライブラリの使い方をざっくりと追いかけながら、hentaiwebサイトを作っていこうと思います。

  • webフレームワーク compojure
  • テンプレートエンジン fleet

Compojure

compojureはclojureで書かれたwebフレームワークです。
sinatraの影響が強く反映されており、シンプルで最小限の機能を提供しています。
どうも、clojure界隈ではデファクトスタンダードらしく、資料の少ないclojureにしてはそこそこ情報が見つかります。

というわけで、こいつとRingで動くwebアプリはこんな感じです。

hentai.core.clj
(ns hentai.core
  (:use compojure.core compojure.response
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))


(defroutes main-routes
  (GET "/" _ "You are hentai")
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

このサンプルを実行すると、デフォルトルートでは、"You are hentai"、 /ikemen にアクセスすると "Your are ikemen" と表示されます。

とりあえず、ルーティングができたのでここにテンプレートエンジンを組み込もうと思います。

Fleet

今回はFleetというテンプレートエンジンを使います。

テンプレートとなるhtml中に<( hogehoge )>といった具合に、<( )>で囲んだ中にclojureのコードを埋め込むことが出来ます。
他にも、テンプレートエンジンはいくつもあるのですが、使い慣れているVelocityに近い書き方ができるので、今回はこれを採用しました。

早速使ってみます

hentai.core.clj
(ns hentai.core
  (:use compojure.core compojure.response fleet
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))

(fleet-ns templates "templates" [:fleet :bypass])

(defroutes main-routes
  (GET "/" _ (templates/hentai {}))
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

ちなみにテンプレートは、hoge.html.fleetの様に最後に.fleetの拡張子をつけます

templates/hentaii.html.fleet
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>This is Hentai web</title>
</head>
<body>
<h1>あなたは変態です</h1>
</body>
</html>

これで、デフォルトルートでhtmlがレンダリングされるはずです

が、なんかエラーが出ました

どうやら、日本語を扱うには、次の表記をcore.cljに追記する必要があるそうです

(extend-protocol Renderable fleet.util.CljString 
    (render [this _] (-> (response (.toString this))
                       (content-type "text-html; charset=UTF-8"))))

ということで、core.cljはこのように

core.clj
(ns hentai.core
  (:use compojure.core compojure.response fleet
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))

(fleet-ns templates "templates" [:fleet :bypass])

(extend-protocol Renderable fleet.util.CljString 
    (render [this _] (-> (response (.toString this))
                       (content-type "text-html; charset=UTF-8"))))

(defroutes main-routes
  (GET "/" _ (templates/hentai {}))
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

もう一息

最後に、fleetに動的に値を渡してみます
core.cljの次の一文は最後に {}が入ってます。

(GET "/" _ (templates/hentai {}))

これは、clojureではMapとして扱われます。
ここに値を追加することで、テンプレートを動的に変更出来ます。
ということで、こんな感じに変更してみます

(GET "/" _ (templates/hentai {:link (get-img)}))

これで、:linkというキーに対して、関数 get-imgの結果が保持されます。
そして、それはそのままテンプレートから呼び出すことができます。

関数get-imgを定義しましょう。
こんな感じに、core.cljに書き足します。

(def hentai-link ["http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854674_photo0.jpg" "http://yipev.com/data/img2/20110408084405/thumb_e1r73nh7fv301cd1uweinzjw.jpg" "http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854662_photo0.jpg"])
(defn get-img []  (get hentai-link (rand-int 3)))

defは定数の定義、defnは関数の定義です。
これで、ベクタhentai-linkからランダムに値を取得する関数get-imgが出来ました。

最終的に、core.cljはこんな感じ

(ns hentai.core
  (:use compojure.core compojure.response fleet
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))

;テンプレートの読み込み
(fleet-ns templates "templates" [:fleet :bypass])

(extend-protocol Renderable fleet.util.CljString 
    (render [this _] (-> (response (.toString this))
                       (content-type "text-html; charset=UTF-8"))))

;ベクタ定義
(def hentai-link ["http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854674_photo0.jpg" "http://yipev.com/data/img2/20110408084405/thumb_e1r73nh7fv301cd1uweinzjw.jpg" "http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854662_photo0.jpg"])
;関数定義
(defn get-img []  (get hentai-link (rand-int 3)))

;ルーティングの設定
(defroutes main-routes
  (GET "/" _ (templates/hentai {:link (get-img)}))
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

templateから、Mapで渡された値を取得するには、dataを使います。

templates/hentai.html.fleet
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Index Fleet</title>
</head>
<body>
<h1>あなたは変態です。</h1>
<img src="<(data :link)>" />
</body>
</html>

これで、ランダムに変態のための画像が表示されるはずです。

まとめ

まとめると、今回の内容は次の通りです。

  • clojureでwebシステム構築してみた
  • compojureを使ってみた
  • fleetを使ってみた

今後は、formに入力してpost取得などをやってみようと思います。
それから、もしfleetに自作関数を食わせる方法をご存知の方がいらっしゃれば
ぜひご連絡ください。
今のところ、値は渡せるようになりましたが変数の渡し方がよくわかっていません・・・


次のリンクで今回作ったhentaiWebをしばらく見れるようにしておくので、
我こそは、という変態の方はぜひご覧ください。
http://kuchitama.ddo.jp

と思ったら、うまく動きませんでした…orz
ローカル環境ではちゃんと見えるので、サーバの設定?
近いうちに直します・・・

直しました。

Leiningenが便利すぎる [[clojure]] [[Leiningen]]

最近、ClojureというLisp系のプログラミング言語を触っています
で、ClojureにおけるMaven的なツールであるLeiningen(http://github.com/technomancy/leiningen)を使っているのですが、めちゃ便利です。
ひょっとしたら、mavenをラップしているだけかもしれませんが、便利には違いありません。

インストール

インストールは超簡単です

bash$ curl -O http://github.com/technomancy/leiningen/raw/stable/bin/lein
bash$ chmod +x lein

あとは、パスを通してやればすぐに使えます。

コマンド

とりあえず、コマンドはこんな感じ

new (projectname) プロジェクト新規作成
deps 依存ライブラリ取得
repl replを起動する
test テストを実行する

clojar(http://clojars.org/)という、clojureのライブラリをまとめたサイトで、検索して、プロジェクト内のproject.cljにペーストして、

lein deps

これだけで、依存ライブラリの問題が解決します。

Eclipseプロジェクト作成

さらに、LeiningenでEclipseのプロジェクトも作成出来ます。

clojure.cljに
:dev-dependencies を追加

(defproject app-name "1.0.0-SNAPSHOT"
  :description "Write Description"
  :dependencies [
        [org.clojure/clojure "1.1.0"]
        [org.clojure/clojure-contrib "1.1.0"]
  ]
  :dev-dependencies [
        [lein-eclipse "1.0.0"]
  ]
)

terminalから、次を実行

lein deps
lein eclipse

あとは、LeiningenのプロジェクトフォルダをEclipseからインポートするだけ!