おさかなせいざ

プログラミングメモや日記がわりに

Clojure * duct * opencv で高専時計(美人時計)を作った

この記事は Clojure Advent Calendar 2017 12日目の記事です。

※注:半分くらい日記、技術内容は下のほうなので、そこだけ見られたい方は本題までホイールをギュインとして下さい。

高専

先月11月、私が通う高専では一般にいう文化祭である高専祭が行われました。

初日は雨が降ったり、そのせいであまり人が来なかったり、子どもの台パンで機材が壊れないかハラハラしておりました。 二日目は天候にも恵まれて、公私共々楽しめるものになったのではないかと思います。

さて、この高専祭には伝統的に高専祭期間中だけに活動する高専祭部活というものがあります。 その中に E project (通称:Eプロ)という本校の4学科ある中の電気情報工学科に所属する学生だけで構成される部活があり、私はここに所属しています。 学科にちなんで、回路の体験コーナや、VR、去年度から続けて音ゲーなんてものも作って展示していました。

もちろん、情報とつくからにはインターネットでの展示も必要ということで、数年前からWebページも作り公開していたわけです。 ここでようやく題名がでてきます。

高専時計

公開しているWebページ、これが高専時計です。

ようこそ高専時計へ | Welcame to Kosen Clock

高専時計は、高専祭に向けて準備している学生や学校関係者の姿を写真に収め、それらに時刻を表示するようにして公開するというものです。 少し古いかもしれませんが、美人時計というと想像がつきやすいのではないでしょうか。 上のリンクから見られます。

今年も高専時計をやるとなり、気になっていた私は参加することにしました。 参加するからには新しいことに取り組みたいと考え、こんな提案をしました。

「当日の来校者にも高専度計に参加してもらおう」

これがアホみたいに自分を苦しめることになります。

今まで通り、学生の姿を収めることはもちろん、当日来て頂いた方たちにもスマートフォンから画像をアップロードしていただいて、それも時計にして表示する。 新しい取り組みとしては良かったと思っています。

ですが、突然他の部活に引き抜かれてデザイン担当が消えたり、他の展示の準備を手伝って時間がなくなったり、 そもそも参加人数が考えていたよりも少なく参加者に配る予定だったお菓子が余りまくったり、画像に時刻を埋め込む位置が全然違う場所になって

と言われたり、これらの片鱗はサイトからも解ると思います。 それでも、二日間で合計1500PVと100枚以上の画像をアップロードをして頂き、高専祭当日の雰囲気も感じられるサイトになりました(主観)。

本題

といったことをするために、考えなければいけなったのは環境です。 使用言語には現在もはまりにはまっている Clojure を使用しました。 理由は使いたかったから。

PHP でやればもっと簡単にできるやん。」

と言われたこともありましたし、実際そうすればよかったと作製中何度も思いましたが、結果的に Clojure で書ききりました。 夏休み前に少し書き動くようにし、💪を構成する基本単位達のインターンでこの話をしたのですが、残念ながら落ちてしまいました。 インターン行きたかった…。 次はもう少し力を付けて行きます。

プログラムの全体は大きく3つに分けられます。

  • 各URLの処理管理を行うルーティング部
  • html の生成を行うテンプレート部
  • 時刻を埋め込む画像処理部

テンプレート部とパーサ部はごちゃ混ぜになって routing.clj にあります。 画像処理部は img.clj になります。 使用した主なライブラリは

ルーティング部に duct

GitHub - duct-framework/duct: Server-side application framework for Clojure

テンプレート部にSelmer

GitHub - yogthos/Selmer: A fast, Django inspired template system in Clojure.

画像処理部にOpencCV

OpenCV library

の3つです。

ディレクトリの構成は以下の通りです。duct のテンプレートを使用しました。

.
├── dev
│   ├── resources
│   │   ├── dev.edn
│   │   └── local.edn
│   └── src
│       ├── dev.clj
│       ├── local.clj
│       └── user.clj
├── img
│   ├── local
│   └── show
├── lib
│   ├── Imshow.jar
│   ├── libopencv_java320.dylib
│   ├── libopencv_java320.so
│   └── opencv-320.jar
├── logs
│   └── dev.log
├── Procfile
├── profiles.clj
├── project.clj
├── README.md
├── resources
│   └── kosen_clock
│       ├── config.edn
│       ├── private
│       │   └── img
│       │       ├── fes52ndmarkA.png
│       │       ├── fes52ndmarkB.jpg
│       │       └── fes52ndmarkC.jpg
│       ├── public
│       │   ├── css
│       │   │   ├── bootstrap.css
│       │   │   ├── bootstrap.css.map
│       │   │   ├── bootstrap.min.css
│       │   │   ├── bootstrap.min.css.map
│       │   │   ├── bootstrap-theme.css
│       │   │   ├── bootstrap-theme.css.map
│       │   │   ├── bootstrap-theme.min.css
│       │   │   ├── bootstrap-theme.min.css.map
│       │   │   └── style.css
│       │   ├── fonts
│       │   │   ├── glyphicons-halflings-regular.eot
│       │   │   ├── glyphicons-halflings-regular.svg
│       │   │   ├── glyphicons-halflings-regular.ttf
│       │   │   ├── glyphicons-halflings-regular.woff
│       │   │   └── glyphicons-halflings-regular.woff2
│       │   ├── img
│       │   │   └── no-image.jpg
│       │   ├── js
│       │   │   ├── bootstrap.js
│       │   │   ├── bootstrap.min.js
│       │   │   └── npm.js
│       │   └── test
│       │       └── test.html
│       └── template
│           ├── base.html
│           ├── list.html
│           ├── non-upload.html
│           ├── not-found.html
│           ├── root.html
│           └── upload.html
└── src
    └── kosen_clock
        ├── boundary
        ├── handler
        │   ├── not_found.clj
        │   └── routing.clj
        ├── img.clj
        ├── info.clj
        ├── main.clj
        └── port.clj

以下ではそれぞれのライブラリの使用した内容や気づいたことについて述べます。

ルーティング部

ルーティング部には duct を使用しました。 正確に言えば duct はルーティングライブラリではありません。 duct は Clojure でhttp の処理を行う際に使われる ring 、 compojure を integrant のデータ駆動アーキテクチャ(よくわかってない)という方法を用いて データの定義や管理、integrant-replを用いて repl 駆動開発を行う際に使用するライブラリです。

duct に関して現在日本語で公開されている情報は、全体的に少し古い情報が多いです。 最新のバージョンで使用する際には、公式の readme や Wiki を参考にするほうがよいと思います。

(defmethod ig/init-key :kosen-clock.handler/routing [_ _]
  (context @access-root []
    (wrap-cookies
     (wrap-multipart-params
      (routes
       (GET "/" [] (parser/render-file "template/root.html"
                                       {:access-root @access-root
                                        :global-last-upload-image @global-last-upload-image}))
        (GET "/upload" {cookies :cookies}
             {:cookies {"markA" {:max-age 604800
                                 :value (let [v (:value (cookies "markA"))] (if-let [tmp (some #(when (= v %) %) (into @making-img @root-imgs))]
                                                                              tmp
                                                                              ""))}
                        "markB" {:max-age 604800
                                 :value (let [v (:value (cookies "markB"))] (if-let [tmp (some #(when (= v %) %) (into @making-img @root-imgs))]
                                                                      tmp
                                                                      ""))}
                        "markC" {:max-age 604800
                                 :value (let [v (:value (cookies "markC"))] (if-let [tmp (some #(when (= v %) %) (into @making-img @root-imgs))]
                                                                      tmp
                                                                      ""))}}
              :headers {"Content-Type" "text/html; charset=utf-8"}
              :body (upload-html {:last-markA (let [v (:value (cookies "markA"))] (some #(when (= v %) %) (into @making-img @root-imgs)))
                                  :last-markB (let [v (:value (cookies "markB"))] (some #(when (= v %) %) (into @making-img @root-imgs)))
                                  :last-markC (let [v (:value (cookies "markC"))] (some #(when (= v %) %) (into @making-img @root-imgs)))})})
        (POST "/upload" {params :params cookies :cookies}
              (upload-image (get params :mark) (get params "file") cookies))
        (GET "/list" {params :params}
             (make-list (get params :page)))

        (GET "/festival" [] (res/redirect (str "http://tokei.maizuru-ct.ac.jp") :moved-permanently))
        (context "/festival/" []
                 (GET "*" [] (res/redirect (str "http://tokei.maizuru-ct.ac.jp") :moved-permanently)))

        (context "/img" []
                 (route/files "/" {:root @img-root})
                 (route/resources "/" {:root "kosen_clock/public/img"})
                 (route/not-found {:headers {"Content-Type" "image/jpeg"}
                                   :body no-image}))
        (route/resources "/public/" {:root "kosen_clock/public"})
        (route/resources "/fonts/" {:root "kosen_clock/public/fonts"})
        (route/resources "/test/" {:root "kosen_clock/public/test"})
        (route/resources "/css/" {:root "kosen_clock/public/css"})
        (route/resources "/js/" {:root "kosen_clock/public/js"}))))))

これが実際に使用したルーティング部です。 duct は ring のラッパの側面もあるので、ここはまんま ring です。 最初の defmethod ig/init-key :kosen-clock.handler/routing ig/init-key が integrant に関数の登録を行う部分です。 config.edn で役割を定義しています。 これらを定義することで、コードの変更を行っても直ぐに repl 上から反映することができます。

以下が config.edn の中身です。

{:duct.core/project-ns  kosen-clock
 :duct.core/environment :development

 :duct.module/logging {:environment :development}
 :duct.module/web {}

 :duct.router/cascading
 [#ig/ref :kosen-clock.handler/routing]

 :duct.handler.static/not-found {:response "This page cannot be found"
                                 :headers {"Content-Type" "text/html; charset=utf-8"}
                                 :body #ig/ref :kosen-clock.handler/not-found}

 :kosen-clock.handler/routing {}
 :kosen-clock.handler/not-found {}
 :kosen-clock/port {}
 }

使用するプラグインなどもここで指定します。

使用してみて、変更をすぐに反映したり、複雑な設定が必要な部分を担ってくれて大変ありがたかったです。

テンプレート部

テンプレート部には Selmer を使用しました。 Selmer はテンプレート元となるhtmlデータに処理を行い、コードを返すというシンプルなライブラリです。

例えば、使用するヘッダは同様ということは多いです。その時に、

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
    <body>
    {{body|safe}}
    </body>
</html>

というテンプレートファイルを用意しておき、

(selmer.parser/render-file "template.html" {:body "<p>hello</p>"})

とすると

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
    <body>
    <p>hello</p>
    </body>
</html>

と返ってきます。もちろん<>がそのまま埋め込まれるのは危険なので、通常は &lt &gt に置換されます。 今回であれば、必要であるのでテンプレートにsafe オプションを入れることでそのままタグとして使用しています。

Selmer 以外にも テンプレートライブラリはありますが、個人的に一番シンプルで分かりやすかったので使用しました。

画像処理部

ほとんどjavaです。もちろん Clojure で書いたのですが、画像処理に使用した Opencv が参照を使って中を直接弄りまくるせいで Clojure っぽさがほとんどありません。 java のラッパを使っているのですから(そもそも C++ ですし)当たり前です。

例えば以下を見て下さい。

(dorun (map (fn [r]
    (let [sm (.submat dst r)
           smc (.submat dst-clone r)
           tmp (Mat.)]
       (Imgproc/GaussianBlur smc smc (Size. 31 31) 8 6)
         (.release sm)
         (.release smc)
         (.release tmp)))
       @roiRects))

これは時刻を表示する位置にぼかし処理を行う部分なのですが、なんだよ release って! python のラッパのように戻り値を採用してほしかったです。 このような処理がたーくさんあり、今読み返しても何を書いているのかわからないところが多いです。

使用する言語選択を失敗した悪い例ではないでしょうか。 目的にあった言語を選びましょう。

まとめ

どのライブラリも使いやすく、期限ギリギリではありましたが高専時計を完成させることが出来ました。 html や css の知識が足りず少し物足りない見た目にはなってしまいましたが、 自分の中で、webサービスとしてここまで大きな物を完成されられたことはいい経験となりました。

教訓:人の管理はちゃんとしよう。

追記

最後に高専時計のソースコードは以下にありますので、よろしければご覧ください。

github.com

明日は anolivetree さんの AndroidClojureを使う話 です。 楽しみ。

最近知ったこととしたこと

イカで述べるClojureはversion 1.8.0 についてである。

Clojure について

Clojure の (case example ...)は (condp = example ...)と等価でない。 Enum の比較を行う際に case ではうまく動作しなかったが、condp であれば動いた。 理由がわからないままなので、Clojureメッチャデキル方はぜひとも教えて頂ければ幸いです。

JavaFX について

と書いたくせにまたClojureなんだけど、Clojurejavafx.scene.control.Label を使おうとして

(def label (Label. "Label"))

とかしたらコンパイルできずに怒られもしないし動かない。

Java 8 で Clojure を使う (2): JavaFX - tnoda-clojure

によると、これはLabelだけでなく、control に含まれるクラス全体らしい。 なのでこれを解決する場合する場合には

(def label (promise))

(defn init []
  (deliver label (Label. "Label")))

のようにすると動いたがバットノウハウ感がある。

JavaFX について

 今度はちゃんと JavaFX についてで、Scene には標準で setOnKeyPressed というキーボードが押された際に発火するhandlerを設定できるメソッドがあるが、 これは KeyCode を含まない。これは KeyEvent/KEY_PRESSED にもともと含まれていないことに起因する。 なので Scene 上で KeyCode が欲しかったら addEventFilter で KeyEvent/KEY_TYPED をとってやるハンドラを設定することで解決できる。 「JavaFXはク○」とか浅はかな知識で思ってごめんなさい。

PDF発表ツール

 良いっぽいのがあった

GitHub - pdfpc/pdfpc: A presenter console with multi-monitor support for PDF files.

けど自分の環境では動かなかったため、ClojureでPDF発表のためのツール開発を初めた。

github.com

上のClojureJavaFXを触っているのはこれを作るため。 現状はPDFの表示とページの移動まで実装できた。 もし気になったりすることがあったら使ってみて下さい。

基本情報とった

基本情報技術者試験してきた

 お久しぶりです。気が向いたときにしか更新しない(ネタがない)ので、記事数が増えないのが最近の悩みです。

 そんななか先日、4/16に基本情報技術者試験を受験しに大阪に行ってました。5/17に合格発表があり、どきどきしながら結果を確認すると無事合格しておりました。 これを記事にせんかったら何を記事にすんねん、と言われた気がしたので行くまでとか行ってきたとか試験対策とかそこらへんを書きます。

続きを読む

謀ったな NVIDIA !!!

やられた

お昼前のことです。Arch linuxX11が起動しても一瞬で止まり、画面が動かず、うんともすんとも言わなくなりました。

続きを読む

セールスマン問題を遺伝的アルゴリズムで解いてみた

セールスマン問題

 セールスマン問題はWikipediaに説明があるのでそちらを見ていただければと思います。
巡回セールスマン問題 - Wikipedia
いくつかの都市を少ないコストで周るにはどの順番でいけばよいか、という問題です。人がやるよりプログラムにやらせたほうが楽そうです。

遺伝的アルゴリズム

 今もよくわかっていません。生き物の進化を参考にして、いい個体といい個体を混ぜて新しい個体を作り、たまに突然変異で中身変えてより良い個体を作ろう、みたいなのが遺伝的アルゴリズムの大まかな認識です。 人が頑張って考えんでもプログラムが勝手に色々試行錯誤してくれるとか最高やん。遺伝的アルゴリズムを使ってなんか解きてぇと考え、セールスマン問題を見つけました。
 こういった内容の記事はたくさんありますが、せっかく解いたので載せてみます。言語はClojureです。

書く

 書いたソースコードを示します。

セールスマン問題をといてみた

 英語がさっぱりなので Google 翻訳を頼りに関数名つけてるけど、めんどくさいです。圧倒的英語力がほしい。
 都市の数は20(要素の数も同様)、1世代100体を1000世代行いました。 都市間の距離は固定するためにそのままリストとして埋めました。 コストが最も少なくなるものを求めます。選択方式はルーレット方式が良さそうだったのでこれに決めました。 突然変異、交叉の確率についてはいくつか試してこれくらいがいい結果を速く得られたので、特に値に理由はありません。(交叉は9割程度がよいと書いてあったところもあったのですが、あまり良い結果を得られませんでした) 実行毎に結果は変わりますが、大体最初は Cost: 50000 くらいから始まっていました。

実行

 最も良かった結果としては

$ lein run
problem: (2138 1198 2667 8293 644 2644 9025 7551 2313 200 1176 501 1364 3016 525 4364 1576 6120 2672 4621 5656 6575 352 5682 6825 6858 1607 2881 3879 3760 424 2627 2055 7930 2642 97 4653 9971 2097 5441 4536 3917 710 195 405 7861 1482 5260 58 4534 5229 696 1711 1696 278 5341 4892 3356 9323 1288 4441 8054 9194 4144 4698 9673 7337 5133 6095 9610 3524 5142 3050 6517 6484 4033 319 1267 2949 751 4784 340 952 1083 1013 387 134 249 1874 368 9259 6963 9 5394 5200 4081 2004 8132 8228 8474 4734 9493 5628 9426 2566 197 9341 479 4821 8767 3667 4413 5249 9557 5846 4856 9110 6405 5826 848 8933 3772 1401 2623 6982 7329 1519 7881 1075 276 3602 7835 470 9977 414 1444 283 1931 8191 4425 6096 4485 589 6870 5281 8295 2284 9578 7887 9042 2033 4587 7949 623 7421 9818 5490 8671 2585 1229 640 1111 5785 8392 3857 3187 5869 1094 464 9391 5375 5128 3439 3601 3306 5301 5746 1784 2485 2367 2325 8722 4754 8540 2342 1308 2295 3124 4279 6214 4874 5791 6 8973 887 96 9121 4863 4767 3908 1518 9347 4525 6652 4397 7324 968 5432 9755 5909)
第 0 世代
DNA:  [14 18 7 4 17 6 12 8 1 16 15 19 9 3 13 5 10 2 0 11] 
Cost:  55250 

第 2 世代
DNA:  (14 18 4 10 9 6 1 12 17 16 2 7 13 3 19 8 5 15 0 11) 
Cost:  49160 

第 10 世代
DNA:  (14 18 4 10 9 6 17 12 1 16 5 8 13 3 19 7 2 15 0 11) 
Cost:  39874 

第 14 世代
DNA:  (14 18 4 10 9 6 7 12 19 16 2 8 13 3 17 1 5 0 15 11) 
Cost:  38202 

第 29 世代
DNA:  (14 18 4 10 9 6 7 12 19 3 2 8 13 16 17 1 5 0 15 11) 
Cost:  33456 

第 35 世代
DNA:  (14 18 4 10 9 6 7 12 13 16 2 8 19 3 17 1 5 0 15 11) 
Cost:  33019 

第 37 世代
DNA:  (14 18 4 1 9 6 7 12 19 3 2 8 13 16 17 10 5 0 15 11) 
Cost:  31189 

第 43 世代
DNA:  (14 18 4 1 9 6 2 12 19 3 7 8 13 16 17 10 5 0 15 11) 
Cost:  28580 

第 66 世代
DNA:  (14 18 4 1 9 6 2 12 19 3 7 8 13 17 16 10 5 0 15 11) 
Cost:  27075 

第 69 世代
DNA:  (14 18 4 1 9 6 2 12 19 3 7 8 13 17 10 16 5 0 15 11) 
Cost:  26202 

第 157 世代
DNA:  (14 18 4 1 9 6 2 12 19 3 7 8 13 17 10 0 5 16 15 11) 
Cost:  25333 

第 196 世代
DNA:  (14 18 4 1 9 6 2 12 19 3 7 8 13 17 10 16 15 0 5 11) 
Cost:  25181 

第 221 世代
DNA:  (14 18 7 8 9 6 2 12 19 3 4 1 13 17 10 0 5 16 15 11) 
Cost:  24208 

第 230 世代
DNA:  (14 18 7 8 9 6 2 12 19 3 4 1 13 17 10 16 15 0 5 11) 
Cost:  24056 

第 252 世代
DNA:  (14 18 7 6 9 8 2 12 19 3 4 1 13 17 10 0 5 16 15 11) 
Cost:  23252 

第 270 世代
DNA:  (14 18 7 6 9 8 2 12 19 3 4 1 13 17 10 16 15 0 5 11) 
Cost:  23100 

第 296 世代
DNA:  (14 18 7 6 9 8 2 12 19 3 4 13 1 17 10 16 15 0 5 11) 
Cost:  22489 

第 330 世代
DNA:  (14 18 7 6 9 8 5 12 19 3 4 13 1 17 10 0 2 16 15 11) 
Cost:  21722 

第 455 世代
DNA:  (14 18 7 6 9 8 5 12 19 3 4 13 17 1 0 10 2 16 15 11) 
Cost:  21714 

第 471 世代
DNA:  (14 18 7 6 9 8 5 12 19 3 4 13 1 17 2 0 10 16 15 11) 
Cost:  21236 

第 525 世代
DNA:  (14 18 7 6 9 8 5 0 19 3 4 13 1 17 12 2 10 16 15 11) 
Cost:  21154 

第 536 世代
DNA:  (14 18 7 6 9 8 5 0 19 3 4 13 1 17 10 2 12 16 15 11) 
Cost:  20924 

第 572 世代
DNA:  (14 18 7 6 9 0 5 8 19 3 4 13 1 17 10 2 12 16 15 11) 
Cost:  20684 

第 587 世代
DNA:  (14 18 7 6 3 0 5 8 19 9 4 13 1 17 10 2 12 16 15 11) 
Cost:  19013 

第 608 世代
DNA:  (14 18 7 6 3 0 5 8 9 19 4 13 1 17 10 2 12 16 15 11) 
Cost:  17819 

第 623 世代
DNA:  (14 18 7 6 3 0 5 8 9 19 4 13 1 17 10 16 12 2 15 11) 
Cost:  17577 

第 642 世代
DNA:  (14 18 7 6 3 0 5 8 9 19 13 4 1 17 10 16 12 2 15 11) 
Cost:  16552 

第 707 世代
DNA:  (14 18 7 6 3 0 5 8 9 19 13 17 1 4 10 16 12 2 15 11) 
Cost:  16548 

第 731 世代
DNA:  (14 18 7 6 3 0 5 8 9 19 4 13 17 1 12 2 10 16 15 11) 
Cost:  14669 

FINISH

となりました。一番最初はランダムですが最終的にその1/4程度になるのはすごい。 ちゃんと値が1対1で対応して交換されていて(交叉したものが残って、あとの世代で突然変異しているものもあります)、交叉、突然変異しているのが目に見て取れます。

すごいけどもしかして…

 たしかにすごいけど、これってランダムで同じくらいの時間まわしたらもっといい結果を得られるんじゃ…?
ということで試してみました。気づいた方もおられると思いますが、ソースコードの(cond (< prob 0) ~となってる部分がランダムの確率の部分です(このままではランダムにはならない)。これを(< prob 1)に変更するとすべてランダムに次世代を生成します。 一世代は同じく100体で、遺伝的アルゴリズムと時間が同じくらいになるよう10000世代にしました。 こちらも何度か試し、最もよかった結果は以下のようになりました。

$ lein run
problem: (2138 1198 2667 8293 644 2644 9025 7551 2313 200 1176 501 1364 3016 525 4364 1576 6120 2672 4621 5656 6575 352 5682 6825 6858 1607 2881 3879 3760 424 2627 2055 7930 2642 97 4653 9971 2097 5441 4536 3917 710 195 405 7861 1482 5260 58 4534 5229 696 1711 1696 278 5341 4892 3356 9323 1288 4441 8054 9194 4144 4698 9673 7337 5133 6095 9610 3524 5142 3050 6517 6484 4033 319 1267 2949 751 4784 340 952 1083 1013 387 134 249 1874 368 9259 6963 9 5394 5200 4081 2004 8132 8228 8474 4734 9493 5628 9426 2566 197 9341 479 4821 8767 3667 4413 5249 9557 5846 4856 9110 6405 5826 848 8933 3772 1401 2623 6982 7329 1519 7881 1075 276 3602 7835 470 9977 414 1444 283 1931 8191 4425 6096 4485 589 6870 5281 8295 2284 9578 7887 9042 2033 4587 7949 623 7421 9818 5490 8671 2585 1229 640 1111 5785 8392 3857 3187 5869 1094 464 9391 5375 5128 3439 3601 3306 5301 5746 1784 2485 2367 2325 8722 4754 8540 2342 1308 2295 3124 4279 6214 4874 5791 6 8973 887 96 9121 4863 4767 3908 1518 9347 4525 6652 4397 7324 968 5432 9755 5909)
第 0 世代
DNA:  [19 2 7 18 11 1 8 13 6 3 0 17 4 10 14 15 9 16 5 12] 
Cost:  56356 

第 1 世代
DNA:  [15 1 4 19 3 2 16 9 14 18 13 17 12 0 10 6 7 5 8 11] 
Cost:  49941 

第 5 世代
DNA:  [12 11 15 16 10 2 0 17 9 19 8 1 13 14 4 5 3 18 7 6] 
Cost:  49602 

第 16 世代
DNA:  [14 9 6 2 7 3 4 19 8 11 15 0 5 12 17 1 18 13 16 10] 
Cost:  38679 

第 583 世代
DNA:  [3 11 7 18 9 19 8 5 12 1 0 2 15 16 10 17 4 14 13 6] 
Cost:  35524 

第 6787 世代
DNA:  [10 18 14 4 12 11 15 16 17 13 1 8 5 0 9 19 2 7 6 3] 
Cost:  32775 

FINISH

うそだろ…?時間かかる上に効果も得られない、と言う結果になりました。最初の半分以下のコストにもならないとは思わなかったです。遺伝的アルゴリズムすごい。

終わりに

 突然変異をどうすればよいか調べると、現在の個体から選んで突然変異させたり、交叉でできた個体を突然変異させると書いてたり、よくわかりませんでした。なので、現在の個体から選び、要素を2つ選んで順番を交換させる方法を選びました。
 わからないなりにも、遺伝的アルゴリズムのすごさが分かりよかったです。

Clojure から Java の可変長引数をもつ関数の呼び出し

はてなブログ初めました。うおざです。
最初の記事に何を書こうか書こうかと考えていたら、いい感じに問題にあたったのでそれを書くことにしました。
それは ClojureJavafx を呼びだしてファイルダイアログを表示させる際に、拡張子でフィルタをかけるようにしていたときのことです。
Clojure で以下のようなコードを書いていました。

(let [fileChooser (FileChooser.)]
  (-> fileChooser
    .getExtensionFilters (.add (FileChooser$ExtensionFilter. "Audio Files" "*.wav" "*.mp3" "*.aac"))))

しかし、これを実行すると

java.lang.IllegalArgumentException: No matching ctor found for class javafx.stage.FileChooser$ExtensionFilter, compiling:(scoreditor/application.clj:67:34)
Exception in thread "main" java.lang.IllegalArgumentException: No matching ctor found for class javafx.stage.FileChooser$ExtensionFilter, compiling:(scoreditor/application.clj:67:34)

といったエラーがでます。

No matching ctor found for class javafx.stage.FileChooser$ExtensionFilter

クラスが見つからないようなことを言われています。
しかし、調べても Clojure でのインナークラスのコンストラクタの呼び出しは

(ClassName$InnerClass.)

で可能のはずなので不思議で不思議でした。Java で無理やり解決しましたが、やはりモヤモヤは消えず。
あまりにもわからないのでtwitterでつぶやいたら


リプライがきました。
Clojure で調べたら日本語の情報で度々目にする @_ayato_p さんです。
最初のコード送ると以下の StackOverFlow の URL がおくられてきました。ありがとうございます。
stackoverflow.com
ふんふんとわからない英語を読むと、どうやら Clojure から Java の可変長引数の関数を呼ぶにはどうすればいいかという内容でした。
Clojure から Java の可変長引数を呼ぶ際には可変長引数の部分を vector で囲んでやらんといけんとのことでした。
FileChooser$ExtensionFilter のコンストラクタを確認してみると可変長引数になっていました。
そこで以下のように直すと

(let [fileChooser (FileChooser.)]
  (-> fileChooser
    .getExtensionFilters (.add (FileChooser$ExtensionFilter. "Audio Files" ["*.wav" "*.mp3" "*.aac"]))))

エラーなく実行できました。[] で囲っただけです。とっても単純。
こういった言語のルールをしらないと今日のように半日うんうん悩むことになってしまうので、しっかりと勉強しておきます。

これからもこういった記事を書いていきますので、よければ見ていただければと思います。よろしくお願いします。