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は動くかな?
後で試してみよう。

2014年4月6日日曜日

[OCaml][Common Lisp][Ruby][SICP]両替の組み合わせを数えるプログラムの実行速度

SICPにあった、両替の組み合わせを数えるプログラムを、Common Lisp、OCaml、Rubyで書いて、実行速度を比べてみた。

各言語のコード

Common Lisp

(defun change-count (amount)
  (change-count-aux amount 5))

(defun change-count-aux (amount kind)
  (cond
    ((= amount 0) 1)
    ((< amount 0) 0)
    ((= kind 0) 0)
    (t (+ (change-count-aux amount (1- kind))
      (change-count-aux (- amount (first-denomination kind)) kind)))))

(defvar *money* #(1 5 10 25 50))

(defun first-denomination (kind-of-coins)
  (elt *money* (1- kind-of-coins)))


OCaml

open Core.Std

let money = List.to_array [1; 5; 10; 25; 50]

let change_count amount =
  let first_denomination kind = money.(kind - 1) in
  let rec count amount kind =
    match (amount, kind) with
    | (0, _) -> 1
    | (amount', _) when amount' < 0 -> 0
    | (_, 0) -> 0
    | (_, _) -> (count amount (kind - 1))
                + (count (amount - (first_denomination kind)) kind)
  in
  count amount (Array.length money)
   
let () =
  print_endline "*start";
  flush stdout;
  printf "Change count 1000=%d\n" (change_count 1000)


Ruby

#!/usr/bin/env ruby

Money = [1,5,10,25,50]

def first_denomination(kind)
  Money[kind - 1]
end

def count(amount, kind)
  if amount == 0
    1
  elsif amount < 0
    0
  elsif kind == 0
    0
  else
    count(amount, kind - 1) + count((amount - first_denomination(kind)), kind)
  end
end

def change_count(amount)
  count(amount, 5)
end

print "*start\n"
printf("Change count 1000=%d\n", change_count(1000))


実行時間の比較

10ドルを1,5,10,25,50セントで両替する場合の組み合わせ数を求める時間を測定した。

Common Lispは Clozure CL 1.9を使用し、REPL上でtimeマクロで測定した。
OCaml は 4.01を使用。ocamlbuildでnative指定でコンパイルし、timeコマンドで測定した。
Rubyは2.0.0-p353を使用。timeコマンドで測定した。

環境はWindow8のVirtualBox上のDebian Wheezy。
CPUは Intel Core-i5 4200U 1.6GHz。
VirtualBoxには2CPUを割り当て。

Common Lisp

CL-USER> (time (change-count 1000))
(CHANGE-COUNT 1000)
took 4,475 milliseconds (4.475 seconds) to run.
During that period, and with 2 available CPU cores,
     4,752 milliseconds (4.752 seconds) were spent in user mode
         0 milliseconds (0.000 seconds) were spent in system mode
801451

OCaml

satoshi@debian:~/workspace/sicp/ch1$ time ./Ch1.native
*start
Change count 1000=801451
./Ch1.native  2.54s user 0.01s system 99% cpu 2.553 total

Ruby

satoshi@debian:~/workspace/sicp/ch1$ time ruby ch1.rb
*start
Change count 1000=801451
ruby ch1.rb  42.54s user 0.06s system 99% cpu 42.630 total


結果

Common Lisp(CCL1.9): 4.752sec
OCaml4.01: 2.54sec
Ruby: 42.54sec

何の根拠もなく、Common Lispの方がOCamlより速いのだろうと思っていたが、結果は逆で、OCamlの方が1.8倍ほど速かった。
圧倒的にRubyは遅かった。VM上でコードを実行するので、もう少し速いと思っていた。Ruby2.1だともう少し速いのかな?

その他

この規模のプログラムでは、各言語での読み易さには、あまり違いがない。OCamlのmatch 〜 with構文は、少し読み易いかなという程度。
Common LispやRubyと異なり、厳格な型チェックをするOCamlのコンパイルが通った後の、プログラム実行時の安心感は格別だ。