2014年5月31日土曜日

[OCaml]Core_benchでベンチマーク


Core_benchを使ってみたので、コードを残しておこう。
ベンチマークを取ったのは、マージソートをする関数。
Core_benchは
$ opam install core_bench
でインストールできる。

open Core.Std
open Core_bench.Std

let make_random_list seed n len =
  Random.init seed;
  let rec make_list len lst =
    if len = 0 then lst
    else make_list (len - 1) ((Random.int n) :: lst) in
  make_list len []

let rec merge xs1 xs2 =
  match (xs1, xs2) with
    ([], _) -> xs2
  | (_, []) -> xs1
  | (hd1 :: tl1, hd2 :: tl2) ->
    if hd1 < hd2
    then hd1 :: (merge tl1 xs2)
    else hd2 :: (merge xs1 tl2)

let rec merge_sort xs =
  match xs with
    [] -> []
  | [_] -> xs
  | [a; b] ->
    if a <= b then xs
    else [b; a]
  | _ ->
    let divide_list xs = List.split_n xs ((List.length xs) / 2) in
    let (xs1, xs2) = divide_list xs in
    merge (merge_sort xs1) (merge_sort xs2)

let run_bench tests =
  Bench.bench
    tests

let bench () =
  let lst1 = make_random_list 0 100 1000 in
  [ Bench.Test.create ~name:"merge_sort1" (fun () ->
       ignore (merge_sort lst1))
  ]
  |> run_bench

Bench.bench関数の仕様が変わったようで、Real World OCamlに書かれていたコードは動かなかった。
この関数に渡すパラメタで、ベンチマークのカスタマイズできるようだけど、試していない。
utopによる実行結果は、以下の通り。
表形式で実行結果が表示された。

utop[1]> #require "core_bench";;
utop[7]> bench ();;
Estimated testing time 10s (1 benchmarks x 10s). Change using -quota SECS.
┌─────────────┬──────────┬─────────┬──────────┬──────────┬───────────┐
│ Name        │ Time/Run │ mWd/Run │ mjWd/Run │ Prom/Run │   mGC/Run │
├─────────────┼──────────┼─────────┼──────────┼──────────┼───────────┤
│ merge_sort1 │   1.08ms │ 57.29kw │  919.07w │  919.07w │ 218.65e-3 │
└─────────────┴──────────┴─────────┴──────────┴──────────┴───────────┘
- : unit = ()
utop[8]>

ふと思ったのだけど、merge_sort関数をマルチスレッドで動かす、お手軽な方法はあるのかな?

参考


2014年5月21日水曜日

[OCaml]Yahoo financeから株式情報を取得する

HTTPクライアントでコンテンツを取得する方法、正規表現モジュールを使って文字列を抽出する方法が分かったので、
サンプルプログラムとして、Yahoo financeから株式情報を取得する処理を書いてみた。

open Core.Std

(* 指定した株式コード、市場の株式情報をYahoo financeから取得する。 *)
let get_yahoo_stock_info code market =
  let module C = Http_client.Convenience in
  let url = Printf.sprintf "http://stocks.finance.yahoo.co.jp/stocks/detail/?code=%s.%s" code market in
  C.http_get url

let extract pattern str =
  let module P = Pcre in
  let rex = P.regexp pattern in
  (P.extract ~rex:rex str).(1)

(* コンテンツを解析して、市場、会社名、業種、取引単位の組を取得する。 *)
let parse_stock_info content =
  let module P = Pcre in
  let market = extract "<span class=\"stockMainTabName\">(.*?)</span>" content in
  let industory =
    let industory' = extract "yjSb\">(.*?)</dd>" content in
    if String.slice industory' 0 2 = "<a"
    then extract "<a[^>]+>(.*?)<" industory'
    else industory' in
  let company_name = extract "<th class=\"symbol\"><h1>(.*?)</h1></th>" content in
  let unit = extract "<dl class=\"tseDtlDelay\"><dd class=\"ymuiEditLink mar0\"><strong>(.*?)</strong>株</dd>" content in
    (market, company_name, industory, unit)

utopで確認。   

utop[22]> let content = get_yahoo_stock_info "6753" "t" in parse_stock_info content;;                                                                                    
- : string * string * string * string =                                                                                                                                  
("東証1部", "シャープ(株)", "電気機器", "1,000")

結構、スッキリ、簡単に書ける。コードも読みやすい。
ただし、正規表現リテラルがないようなので、\は\\と書かなければならないのが、まどろっこしい。

※2014/5/21現在は、上記の正規表現で株式情報を取得できるが、Yahoo financeのコンテンツの構成が変更されると、取得できなくなる可能性がある。

2014年5月18日日曜日

[OCaml]Coreでusleep

sleepシステムコールでは1秒未満で待つことができないので、usleepを使おうと思ったが、OCamlのUnix標準モジュールではサポートされていなかった。
ここを見るとselectシステムコールを使う例があったので、試してみたら、構文エラーになった。
何で?と思って調べたら、自分の環境では常にCoreを使うようにしていた。
open Core.StdしているとUnix.selectはCore版になるのね...
Coreを使っている場合は以下のようにusleep関数を定義すればOK。

open Core.Std

let usleep time =
  ignore @@ Unix.select ~read:[] ~write:[] ~except:[] ~timeout:(`After time) ()


utopで確認。

utop[14]> let print_now () = print_endline @@ Time.to_string @@ Time.now ();;
val print_now : unit -> unit = <fun>
utop[18]> print_now (); usleep 0.25; print_now ();;
2014-05-18 19:04:40.408135+09:00
2014-05-18 19:04:40.659611+09:00 ←◆0.25sec待機
- : unit = ()

Coreで再定義する前の関数を参照する方法は無いのかな?

参考


2014年5月17日土曜日

[OCaml]PCRE-OCamlのサンプルコード

OCamlの正規表現ライブラリはいくつかあるようだけど、PCRE-OCamlがお手軽そうだったので、使ってみた。
メモしておこう。

PCRE-OCamlはopamでインストールできる。
$ opam install pcre-ocaml

以下、utopでの例。

utop[0]> #require "pcre";;
utop[1]> open Pcre;;

execはsubstringsを返す。

utop[16]> let s = exec ~rex:(regexp "aaa(.*)bbb") "aaaほげbbbほげccc";;
val s : substrings = <abstr>

get_substringsでstring arrayを取得できる。

utop[17]> get_substrings s;;
- : string array = [|"aaaほげbbb"; "ほげ"|]
utop[18]> (get_substrings s).(0);;
- : string = "aaaほげbbb"

マッチしないときは例外が出る。

utop[42]> let s = exec ~rex:(regexp "ほげげ") "aaaほげbbb";;
Exception: Not_found.

exec_allはsubstrings arrayを返す。

utop[36]> let sa = exec_all ~rex:(regexp "aaa(.*?)aaa") "aaaほげ1aaa aaaほげ2aaa";;
val sa : substrings array = [|<abstr>; <abstr>|]
utop[37]> get_substrings sa.(0);;
- : string array = [|"aaaほげ1aaa"; "ほげ1"|]
utop[38]> get_substrings sa.(1);;
- : string array = [|"aaaほげ2aaa"; "ほげ2"|]

マッチした文字列の配列を返すextract, extract_all関数もある。(こちらの関数の方が扱いが簡単。)

utop[48]> extract ~rex:(regexp "aaa(.*?)aaa") "aaaほげ1aaa aaaほげ2aaa";;
- : string array = [|"aaaほげ1aaa"; "ほげ1"|]

utop[49]> extract_all ~rex:(regexp "aaa(.*?)aaa") "aaaほげ1aaa aaaほげ2aaa";;
- : string array array =
[|[|"aaaほげ1aaa"; "ほげ1"|]; [|"aaaほげ2aaa"; "ほげ2"|]|]

UTF-8で日本語を使うときには、フラグ指定で、
regexp ~flags:[`UTF8] "aaa(.*?)aaa"
としておいた方が良いかな?

参考


2014年5月16日金曜日

[OCaml][Emacs]mlファイルとmliファイルを交互に切り替えるelisp

Emacsのtuareg-modeでOCamlのコードを書いている。
編集中はmliファイルまたはmlファイルを頻繁に切り替えているのだけど、switch-buffer(C-x b)で指定するのが面倒になったので、簡単に切り替えることができるelispを作成した。
tuaregには対応するコマンドは、きっとあるだろうなと思い、tuareg-* を探してみたが、見当たらなかった。残念。

キーバインディングは、空いていた C-c , に割り当てた。

(defun switch-file-ext (file-name ext1 ext2)
  (let ((file-ext (file-name-extension file-name)))
    (unless (member file-ext (list ext1 ext2))
      (error "unmatch file extension"))
    (message file-ext)
    (concat (file-name-directory file-name)
            (file-name-base file-name)
            "."
            (if (string= file-ext ext1) ext2 ext1))))

(defun switch-to-ml-or-mli ()
  (interactive)                                                                      (find-file (switch-file-ext (buffer-file-name) "ml" "mli")))
 
(define-key tuareg-mode-map (kbd "C-c ,") 'switch-to-ml-or-mli)

これで、mliファイルまたはmlファイルを編集中に、C-c , をキー入力すると、
例えば、hoge.mli を編集している場合は hoge.ml に、
逆に、hoge.ml の場合は、 hoge.mli に切り替えできるようになった。

こんなふうに手軽に機能拡張できるEmacsは素晴しいね。

2014/5/17 追記
Twitterで、nomaddoさん星のキャミバ様に教えていただいたが、上記の関数を作らなくても、tuareg-find-alternate-fileがあることが判明。
デフォルトでは、C-c C-aに割り当てられていた。
 /(^o^)\ナンテコッタイ

2014年5月14日水曜日

[OCaml]Ocamlnetのhttp_clientでコンテンツを取得する

OCamlで簡単に使えるHTTPクライアントを探していたのだけど、
Ocamlnetライブラリのhttp_client.Convenienceモジュールを使うと、
とても簡単に、コンテンツを取得できた。

Ocamlnetのインストール

Ocamlnetはopamでインストールできる。
$ opam install ocamlnet
現時点のバージョンは3.7.3だった。

Http_client.Convenienceモジュール

Http_client.ConvenienceモジュールにはGETリクエストでコンテンツを取得する関数がある。
詳細はリファレンスマニュアルを参照。

val http_get : string -> string
  Does a "GET" request with the given URL and returns the message body
val http_get_message : string -> Http_client.http_call
  Does a "GET" request with the given URL and returns the message

サンプルコード

utopで試してみた。

utop[0]> #require "netclient";;
utop[1]> Http_client.Convenience.http_get "http://ほげほげ.com/";;
- : string =
"<html><body><h1>It works!</h1>
<p>This is the default web page for this server.</p>
<p>The web server software is running but no content has been added, yet.</p>
</body></html>
"
utop[2]> Http_client.Convenience.http_get_message "http://ほげほげ.com/";;
- : Http_client.http_call = <obj>
utop[4]> let c = Http_client.Convenience.http_get_message "http://ほげほげ.com/";;
val c : Http_client.http_call = <obj>
utop[6]> c#status;;
- : Http_client.status = `Successful
utop[7]> c#response_body;;
- : Netmime.mime_body = <obj>
utop[8]> c#response_body#value;;
- : string =
"<html><body><h1>It works!</h1>
<p>This is the default web page for this server.</p>
<p>The web server software is running but no content has been added, yet.</p>
</body></html>
"
utop[9]>

すごく簡単。

OCamlnetは、HTTPクライアント以外にも、FTP、POP、SMTP等、ネットワークに関連する様々なモジュールを提供している。
リファレンスは分かりやすく、結構便利だ。

参考


2014年5月11日日曜日

[Debian][Java]Debian Wheezy(64bit版)にJava8をインストール

Debian Wheezy(64bit版)の環境を作成中。
Java8をインストールしたのでメモを残しておく。

JDKをhttp://www.oracle.com/techntwork/java/javase/downloads/jdk8-downloads-2133151.htmlからダウンロードする。
2014/5/11時点の最新バージョンは jdk1.8.0_05。

/tmpにダウンロードした。

以下はrootで作業する。
$ mkdir /opt/java-oracle
$ tar -zxf /tmp/jdk-8u5-linux-x64.tar.gz -C /opt/java-oracle
$ export JHome=/opt/java-oracle/jdk1.8.0_05
$ update-alternatives --install /usr/bin/java java $JHome/bin/java 20000
$ update-alternatives --install /usr/bin/javac javac $JHome/bin/javac 20000
インストール結果を確認する
$ update-alternatives --config java
$ java -version

hello,world!で確認

==Hello.java==
public class Hello {
    public static void main(String[] args) {
    System.out.println("hello, world!\n");
    }
}

satoshi@lucy:~/workspace/java$ javac Hello.java
satoshi@lucy:~/workspace/java$ ll
合計 12
-rw-r--r-- 1 satoshi satoshi 418  5月 11 20:54 Hello.class
-rw-r--r-- 1 satoshi satoshi 114  5月 11 20:54 Hello.java
satoshi@lucy:~/workspace/java$ java Hello
hello, world!

とりあえずコンパイル、実行はできた。
Clojureは動くかな?
後で試してみよう。