2013年12月12日木曜日

[Clojure]トランザクションの最大リトライ回数

トランザクション内で、リファレンスの値の変更に失敗した場合は、トランザクションの最初から処理をリトライする。

トランザクションの処理に時間がかかり、何回繰り返してもリファレンスの値を更新できない場合は Live lock 状態になる。Clojureでは、これを防ぐため、リトライ回数がある一定値を越えると、例外を投げる仕組みになっている。

リトライ回数の最大値はどのくらいかなと、調べてみた。

(def x (ref 0))

(defn test1 []
 (dosync
  @(future (dosync (alter x inc)))
  (ref-set x -1)))


user> (test1)
RuntimeException Transaction failed after reaching retry limit  clojure.lang.Util.runtimeException (Util.java:219)
user> x
#<Ref@843d62: 10000>
user>

最大10000回もリトライしていた。
数十回程度かなと思っていたので、少しびっくり。
この値は変更できるのか、ざっと調べてみたが、変更方法は見付からなかった。
Clojure の STM のドキュメントのどこかに書いてあるのかな?

2013年12月7日土曜日

[Clojure]形態素解析ライブラリKuromojiを使う

オーム社の100周年記念セールで、はじめてのAIプログラミング C言語で作る人工知能と人工無能を購入した。2006年に出版された書籍で、C言語(Borland C)での実装が解説してある。
この類のプログラムをCで実装するのは煩雑になってしまうので、Clojureで作ったらどんなふうになるのかなと思い、文字列の解析時に使用する形態素ライブラリを調べてみた。
Lucene や Solr に対応したlucene-gosen が、結構使われているようだけど、mavenリポジトリにあるバージョンは少し古く、また、多くのライブラリに依存していており、それらのラブラリのバージョンも少し古かった。
その点、Kuromojiは、依存ライブラリがなく、lucene-gosenと同様に辞書も同梱されているので、こちらのライブラリを試してみた。

project.cljにKuromojiの設定を追加

Kuromojiをmavenで使うためには、リポジトリの追加が必要である。
project.cljに次の設定を書く。

:repositories [["Atilika Open Source repository"
                "http://www.atilika.org/nexus/content/repositories/atilika"]]
:dependencies [[[org.atilika.kuromoji/kuromoji "0.7.7"]]


リポジトリのURLの追加方法が最初分からず、いろいろ調べたが、
sample.project.cljのサンプルが参考になった。


サンプルコード

(ns gosen.core
  (:import (org.atilika.kuromoji Token Tokenizer)))

(defn -main []
  (let [tokenizer (. (Tokenizer/builder) build)]
    (doseq [token (. tokenizer tokenize "Javaより楽しいClojure。")]
      (println (str (. token getSurfaceForm) "\t"
                    (. token getAllFeatures))))))


何のことはない。Kuromojiのクラスを使う単純なコードだ。

実行結果

replより実行した。

gosen.core> (-main)
Java    名詞,固有名詞,組織,*,*,*,*
より    助詞,格助詞,一般,*,*,*,より,ヨリ,ヨリ
楽しい    形容詞,自立,*,*,形容詞・イ段,基本形,楽しい,タノシイ,タノシイ
Clojure    名詞,一般,*,*,*,*,*
。    記号,句点,*,*,*,*,。,。,。
nil

ユーザ辞書を追加することも簡単なようなので、試してみる予定。

参考

Java製形態素解析器「Kuromoji」を試してみる

2013年12月2日月曜日

[Clojure][Emacs]Clojure Cheatsheet for Emacs

EmacsでClojureのCheatsheetを閲覧できるELispがあった。
Clojure Cheatsheet for Emacs
試しに使ってみたら、結構便利。

インストール

Emacs24なら、MELPA経由で簡単にインストールできる。

Emacs初期化ファイルに以下のような設定を書いておけば良い。

;; MELPAを有効にする
(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)


サイトの説明通り、以下のコマンドでインストールする。
M-x package-refresh-contents
M-x package-install RET clojure-cheatsheet

実行

M-x clojure-cheatsheet を実行すると、
ミニバッファに
pattern:
と表示されるので、検索したい文字列を入力する。
sort mapを入力した場合は、次のように表示される。



キーバインドは次の通り。
C-p 前の行
C-n 次の行
RET 選択した項目の表示

clojure-mode時に有効になるような、キーバインドを書いておくと便利かも。
試していないが、Helmのsourceにも対応している。

2013年12月1日日曜日

[Haskell][Clojure]逆ポーランド記法の解析処理

Learn You a Haskell for Great Good で書かれていた逆ポーランド記法の解析処理をClojureで書いてみた。
四則演算のみサポートする単純な処理である。

Haskellの場合

module RPN where
       
solveRPN :: String -> Double
solveRPN = head . foldl func [] . words
    where
      func (x:y:ys) "+" = (y + x) : ys
      func (x:y:ys) "-" = (y - x) : ys
      func (x:y:ys) "*" = (y * x) : ys
      func (x:y:ys) "/" = (y / x) : ys
      func xs num = read num : xs

*RPN> solveRPN "1 2 + 3 * 1 - 2 /"
4.0
     
whereを使うとコードが分かり易くて、なかなか良いね。

Clojureの場合

(ns rpn.core
  (:use [clojure.string :only (split)]))

(defn words [s]
  (split s #"\s+"))

(defn op2 [[x y & ys] f]
  (conj ys (apply f [y x])))

(defn operate [stack item]
  (case item
    "+" (op2 stack +)
    "-" (op2 stack -)
    "*" (op2 stack *)
    "/" (op2 stack /)
    (conj stack (Double/parseDouble item))))

(defn solve-rpn [exprs]
  (first (reduce operate '() (words exprs))))

rpn.core> (solve-rpn "1 2 + 3 * 1 - 2 /")
4.0

solve-rpn関数の中でoperate関数を定義することも可能だが、かえって分かり難くなりそうなので、外出しして関数定義した。
Haskellに比べると冗長になってしまった。
もっと、すっきり書けるのかな?
文字列のdouble型への変換 (Double/parseDouble item) は美しくない。→と思ったときは parse-double関数を作れば良いのだけど。

2013年11月30日土曜日

[Debian][Haskell]WheezyにHaskell Platform最新版をインストール

Wheezyのghcは少しバージョンが古く、ghc-modをcabal installしたらエラーとなった。
どうしようもなかったので、Haskell Platformの最新版をインストールした。

依存ライブラリのインストール

次のパッケージをapt-get installしておく。
$ sudo apt-get install freeglut3-dev libgmp10-dev

このままだと、ghcのconfigure時に
checking for path to top of build tree... utils/ghc-pwd/dist/build/tmp/ghc-pwd: error while loading shared libraries: libgmp.so.3: cannot open shared object file: No such file or directory
と怒られるので、シンボリックリンクを張る。
$ sudo ln -s /usr/lib/i386-linux-gnu/libgmp.so.10 /usr/lib/libgmp.so.3

ghcのインストール

バイナリパッケージを使う。
http://www.haskell.org/ghc/download_ghc_7_6_3
より、32bit版をダウンロードする。
(私はWheezy 32bit版を使用している。)
http://www.haskell.org/ghc/dist/7.6.3/ghc-7.6.3-i386-unknown-linux.tar.bz2
展開して/usr/localにインストールする。
$ tar jxf ghc-7.6.3-i386-unknown-linux.tar.bz2
$ cd ghc-7.6.3
$ ./configure
$ sudo make install

Haskell platformのインストール

http://www.haskell.org/platform/linux.html
http://www.haskell.org/platform/download/2013.2.0.0/haskell-platform-2013.2.0.0.tar.gz
より、ソースをダウンロードする。
ghcと同様に、展開して/usr/localにインストールする。
$ tar zxf haskell-platform-2013.2.0.0.tar.gz
$ cd haskell-platform-2013.2.0.0
$ ./configure
$ make
コンパイルにはしばらく時間がかかる。
$ sudo make install

ghc-modのインストール

これで、
$ cabal update
$ cabal install ghc-mod
で、ghc-modがインストールできた。

2013年10月9日水曜日

[Clojure][Emacs] Windows環境のnreplで表示される^Mを消す

Windows環境でnreplを使うと、改行文字の^Mが表示されてしまい、とても目障りだ。

user> (dotimes [i 5] (println (str "hello" i)))
hello0^M ←◆これが目障り
hello1^M
hello2^M
hello3^M
hello4^M
nil
user>

Emacs Lispを使いbuffer-display-tableで^Mを表示しないように設定して、対処した。
もっと簡単な方法は無いのかな?

^Mを消すためのEmacs Lisp

(defun remove-dos-eol ()
  "Do not show ^M in files containing mixed UNIX and DOS line endings."
  (interactive)
  (setq buffer-display-table (make-display-table))
  (aset buffer-display-table ?\^M []))

(add-hook 'nrepl-repl-mode-hook
 'remove-dos-eol)

適用後

user> (dotimes [i 5] (println (str "hello" i)))
hello0 ← ^M が消えてスッキリ
hello1
hello2
hello3
hello4
nil
user> 

参考





2013年10月6日日曜日

[Clojure] clj-webdriverでChromeのUser-Agentを変更する方法(その2)

[Clojure] clj-webdriverでChromeのUser-Agentを変更する方法 でUser-Agentを変更する方法を書いたが、先日、最新のChromeとchromedriverを使ってみると、User-Agentが変更できなくなっていた。
原因を調べてみると、DesiredCapabilitiesオブジェクトにchrome.switchesを追加しても、Chromeの起動オプションに追加されなくなっていた。
起動オプションの追加方法が変更されたようで、ChromeOptions#addArgumentsを使うと問題なく動作した。

実行環境

  • Windows 8
  • clj-webdriver 0.6.0
  • chromedriver 2.4
chromedriver 2.4 はこちらからダウンロードした。
従来のダウンロード先にあったドライバは全てdeprecatedになっており、http://code.google.com/p/chromedriver/wiki/WheredAllTheDownloadsGo?tm=2 では、以下の説明があった。
Downloads have been moved to http://chromedriver.storage.googleapis.com/index.html, because Google Code downloads are being deprecated!

project.clj

次のライブラリをdependenciesに追加した。

[clj-webdriver "0.6.0"]
[org.seleniumhq.selenium/selenium-java "2.35.0"]

src/cli_webdriver_patch.clj

前回と同様に、clj-webdriver.coreの関数を再定義して対応する。

(ns clj-webdriver-patch)

(in-ns 'clj-webdriver.core)
(import 'org.openqa.selenium.chrome.ChromeOptions)
(declare new-chrome-driver)

(defn new-driver
  "Start a new Driver instance. The `browser-spec` can include `:browser`, `:profile`, and `:cache-spec` keys.
The `:browser` can be one of `:firefox`, `:ie`, `:chrome` or `:htmlunit`.
The `:profile` should be an instance of FirefoxProfile you wish to use.
The `:cache-spec` can contain `:strategy`, `:args`, `:include` and/or `:exclude keys. See documentation on caching for more details."
  ([browser-spec]
     (let [{:keys [browser profile chrome-switches cache-spec]
            :or {browser :firefox
                 profile nil
                 chrome-switches nil
                 cache-spec {}}} browser-spec]
       (if (= browser :chrome)
         (new-chrome-driver chrome-switches)
         (init-driver {:webdriver (new-webdriver* {:browser browser
                                                   :profile profile})
                       :cache-spec cache-spec})))))
(defn new-chrome-driver
  [chrome-switches]
  (let [option (ChromeOptions.)]
    ;; for Linux
    ;; (.setBinary option "chrome.binary" "/usr/bin/google-chrome")
    (if chrome-switches
      (.addArguments option (into-array chrome-switches)))
    (init-driver (ChromeDriver. option))))


src/chrometest/core.clj

サンプルコード。
User-AgentにiPhoneを設定し、googleで「hogehoge」を検索する。

(ns chrometest.core
  (:require [clj-webdriver.core])
  (:use clj-webdriver-patch)
  (:require [clj-webdriver.taxi :as taxi]))

(System/setProperty "webdriver.chrome.driver" "c:/Application/chromedriver/chromedriver-2.4.exe")

(def ^:dynamic *ua-iPhone-3G* "Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_0_1 like Mac OS X; ja-jp) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5B108 Safari/525.20")

(defn user-agent-switch [user-agent]
 (str "--user-agent=\"" user-agent "\""))

(defn test1 []
  (taxi/set-driver! {:browser :chrome
                     :chrome-switches [(user-agent-switch *ua-iPhone-3G*)]})
  (taxi/to "https://www.google.com/")
  (taxi/input-text "input[name='q']" "hogehoge")
  (taxi/click "button[name='btnG']"))


Debian Wheezyでの実行

chromedriverの最新版はglibc2.1.15に依存しており、glibcのバージョンを上げなければならない。
How to upgrade glibc from version 2.13 to 2.15 on Debian? にglibcのバージョンを上げる方法が書かれていたが、試していない。