パワポに貼る用の数式画像の生成

スライド作成は一時期beamerを使っていたものの面倒になってPowerPointに戻ってきている。Office2007の数式エディタはしょぼいのでdvipngを使う。具体的には次のようなシェルスクリプトMakefileで複数の数式ファイルを管理。(Officeを2010にする以外にも)もっとマシな方法がある気がする。

#!/bin/bash

tmp=`mktemp XXXXX`
echo -e "\documentclass{jarticle}\\n\usepackage{amsmath,amssymb}\\n\pagestyle{empty}\\n\\\begin{document}" | cat - $1 > $tmp
echo -e "\\n\\\end{document}" >> $tmp

platex $tmp
out=`echo $1 | sed -e s/\\\..*/\\\.png/`
dvipng ./${tmp}.dvi -T tight -o $out -D 300 -bd 1000
rm $tmp ${tmp}.*

exit 0

GCJJ2011予選参加

  • 参加id:LennMars
  • 結果:Aで暗算間違えてLarge突っ込んで時間切れ、合計41点。時間あったのにアホですね……
  • 感想:これまでGCJにはOCamlで参加していたのだけどこの手のプログラミングコンテストに関してOCaml(特に32bit環境)は余りにもないということに気付いて1週間前に始めたScalaでやったらえらいやりやすくて感動しました。

ant/Scalaのエラーメッセージをflymakeが正しくパースしてくれない問題

具体的には行数のマッチに失敗して1行目しか赤くしてくれない。これでは使えない(そもそもScalaコンパイル速度が遅くてロクに使えないことは一旦忘れる)。
そもそもflymakeがエラーメッセージのパースに何を使用しているかというと、正規表現をキー、そのsubexpressionsのうち何番目がファイル名やエラー行数に対応しているかを示すリストを値としたalistである。これはcompilation-error-regexp-alistで定義され、flymake.el中で多少拡張して用いられている(flymake-err-line-patterns)。
今回問題を引き起こしていたのはflymake.el側で定義されたant/Java用の

(" *\\(\\[javac\\]\\)? *\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)\:\\([0-9]+\\)\:[ \t\n]*\\(.+\\)" 2 4 nil 5)

であった。Scalaコンパイルエラーメッセージにコロンが入っている事が原因ぽい。最初の?を削除して上書きし、Scala用のやつを新しく付け足し。ついでにcompilation-error-regexp-alistの先頭の

("\\([a-zA-Z][-a-zA-Z._0-9]+: ?\\)?\\([a-zA-Z]?:?[^:( \n]*[^:( \n0-9][^:( \n]*\\)[:(][ ]*\\([0-9]+\\)\\([) ]\\|:\\(\\([0-9]+:\\)\\|[0-9]*[^:0-9]\\)\\)" 2 3 6)

も一般的過ぎて余計なものにまでマッチするのでコメントアウトした。

DevQuiz2011 スライドパズル

  • 結果:3723問
  • 言語:OCaml
  • ソース:https://github.com/LennMars/DevQuiz2011
  • 方針:
    • 普通のA*(評価関数はマンハッタン距離 + 2 * Linear Conflict)
    • といってもこれで素直に解けるのは4*4の一部までだろう⇒早速完答を諦め、2段階に分けて解くことにする
      • 1段階目で正しい位置に持っていきたいピース以外をマスクし、マスクされたピースについては評価関数に加算しないようにしてA*を実行
      • 1段階目が終了したらマスクを外してそこから再度A*を実行
      • マスクのいい決め方は思いつかなかったので盤面の角に接する直方領域を総当たり的に生成して袋小路を埋める感じにした(平均80パターンくらい)。
      • 1つのマスクにつき、訪問済み盤面が一定数を越えたら強制終了(一定数=だいたい1000くらい)
      • 勉強だと思って優先度つきキューとそれを実現するための可変長配列もどき(OCamlの配列サイズ制限に対応するため2次元配列で実装)も自前実装した
    • 短い解が得られた問題から順に手数制限に突き当たるまで選択して回答とする
    • この段階で冗長な手を短縮しようとした努力の結果がshortenディレクトリに入っている
      • 3672問→3723問と一応マシになってはいるが……
  • 結果:
    • 1マスクにつき約0.5秒*80パターン*5000インスタンス=約50時間(CPU:2.40GHz)
    • 使用メモリはせいぜい40MB
    • 7問に1問ほど難しいインスタンスがあったらしく全マスクパターンで失敗した
    • つまり解いたのに解の質が悪いせいで手数制限に引っかかり回答に含められなかったのが600問ほどあるということに
  • 反省:
    • 反復深化を使うべきだったかも
    • せめてマスク生成をもう少し賢くするとか最大訪問済み盤面数を盤面サイズによって変えるとか……
    • 今気付いたんだけどもしかしてこれ劣化双方向探索だった?

Caml trading

Y. Minsky and S. Weeks: Caml trading - experiences with functional programming on Wall Street. JFP 18(4):553-564, 2008.

著者らが所属するJane Streetは自社資金のみでトレーディングを行う会社であり、設立当初はExcelVBAを使っていたが2005年頃からOCamlをメイン言語として採用している。僅かなバグや数ミリ秒の遅れがパフォーマンスに大きく響く自動取引の世界で、安全性と速度の両立を実現したOCamlの特徴とその活用法とは、というのがストーリー。美点と活用法に関してReadability、Performance、Macrosの3節の後OCamlの欠点について、とバランスよく簡潔に書かれている。順に追っていくと、

  • Readabilityについて
    • コードの簡潔さ
    • 不変性
    • パターンマッチング
      • 記述・変更が容易、網羅性チェックがもたらす安全性
    • モジュールシステムとか

あたりの関数型言語への言及に付きもののあれこれに以下のOCamlっぽいトピックが続く。

    • ラベル付き引数
      • 型が同じ引数の混同を避けられる
    • 多相バリアント
      • 例として、例外を用いることを避けて多相バリアントを用い簡潔かつ扱いやすい形でその関数が「失敗」する可能性を表現する手法が紹介されている
    • privateな型, 幽霊型
  • Macrosについて
    • OCamlの型についてS式との相互変換を行う関数の自動生成を可能にするcamlp4拡張が紹介されている。主なご利益は、OCamlに欠如している以下の点の埋め合わせ。
      • generic printer(OCamlにはderiving Showみたいな便利機能はない)
      • 安全なmarshaling/unmarshalingの手段(標準ライブラリにMarshalがあるが自分で型を指定しなければならないという問題がある)

一方欠点については

  • generic printerの欠如
    • 既出だが。macroでなんとかなるけどデフォルトであったら便利だよねと書いてある
  • オブジェクト指向機能の存在
    • いらないよねとの指摘。確かにあまり使わないけど……
  • 最適化の不足
    • aggressiveな最適化をしていないことで性能のために可読性を犠牲にせざるを得ない場面が、という指摘。個人的にはそれはいつもそういうもんだしOCamlコンパイラの単純さも含めたトレードオフに対して十分reasonableな選択をしているのみではという感想。性能重視の書き方をしたときも大抵そんなに無茶苦茶なことにはならないのであまり文句がない
  • 並列実行不可
    • 並列GCがないことによる。Jane Streetとしては作れれば作りたいのだろうけどどれくらい見込みがあるのかは知らない
  • ビルドのサポートが乏しく巨大プロジェクトに不向き
    • モジュールシステム+findlibだけでは不十分との指摘
  • INRIAのチームによる伽藍型の開発
    • もちろん彼らは素晴らしい仕事をしているが一方どうしても貧弱な標準ライブラリなどの原因となってしまっているという指摘。これも良し悪しとしかいいようのない感じがするが……

といったところ。他にも優秀な人材が集めやすかっただとか色々書いてあるのだけど省略。12ページですぐ読めるのでOCamlがどんな言語なのか(構文とかではなくもっと全体的な話)を掴みたい人にお勧めしたい。

Big_int.extract_big_intが変

let n = 100
let b = shift_left_big_int unit_big_int n |> pred_big_int (* 111...11 *)
let k = 2;;

for i = 0 to n - k do
  let m = (extract_big_int b i k |> int_of_big_int) in
  if m <> int_exp 2 k - 1 then Printf.printf "%d : %d\n" i m; (* failed *)
done

とすると

31 : 1
63 : 1
95 : 1

が出力される(OCaml version 3.12.1, Ubuntu 10.04 32-bit)。
extract_big_intはbig_int型の値を2進数表示したものの指定した桁(上ではi)から指定した長さ(上ではk)bitsを切り出す関数であり、例えば2^n-1からならどこから切り出しても連続した1が出てくるはずなのだがそうなっていない、という話。ちなみに64-bitアーキテクチャでは64桁ごとに失敗する。こういう仕様だと言われたらそれはそれで納得できなくもない気もするが。

PFIサマーインターン2011問題の解法の一例

http://research.preferred.jp/2011/07/intern2011_problem/
を分割統治法で解く。

let find_freq x =
  let open String in
  let rec aux x len remain =
    Printf.printf "%s, %d, %c\n" (sub x 0 len) len remain;
    match len with
    | 1 -> get x 0
    | 0 -> remain
    | _ ->
	let rec pair i len_next =
	  if i = len / 2 then (* problem size halved *)
	    (len_next, if len land 1 = 1 then get x (len - 1) else remain)
	  else match get x (2 * i) with
	    c when c = get x (2 * i + 1) ->
	      set x len_next c; (* constant time operation *)
	      pair (i + 1) (len_next + 1)
	  | _ ->
	      pair (i + 1) len_next
	in
	let (len_next, remain_next) = pair 0 0 in
	aux x len_next remain_next
  in
  aux (copy x) (length x) ' '

let x1 = "aabbababa"
let x2 = "aabbaabbaabbaabbccddaaabbabaababaaaaabaa"
let x3 = "baabbaa"
let x4 = "abcaabaca"
let x5 = "abcaabaac"
let xs = [x1; x2; x3; x4; x5];;

List.iter (fun x -> Printf.printf "ans : %c\n\n" (find_freq x)) xs