Brainfuck インタプリタ

寝る前に OCaml でなんか書いてみるか、と思って書いてみた。

exception Unexpected_char
let interpret str =
  (* バッファ *)
  let buf = Array.create 3000 0 in
  (* 対応する右括弧(])を探しに行く *)
  let rec rp idx n = match String.get str idx with
    | '[' -> rp (idx+1) (n+1)
    | ']' -> if n=0 then idx else rp (idx+1) (n-1)
    | _ -> rp (idx+1) n
  (* 対応する左括弧([)を探しに行く *)
  and lp idx n = match String.get str idx with
    | '[' -> if n=0 then idx else lp (idx-1) (n-1)
    | ']' -> lp (idx-1) (n+1)
    | _ -> lp (idx-1) n
  (* 1文字ずつ読み込んで処理 *)
  and loop idx p = match String.get str idx with
    | '>' -> loop (idx+1) (p+1)
    | '<' -> loop (idx+1) (p-1)
    | '+' -> buf.(p) <- buf.(p)+1; loop (idx+1) p
    | '-' -> buf.(p) <- buf.(p)-1; loop (idx+1) p
    | '.' -> print_char(char_of_int(buf.(p))); loop (idx+1) p
    | ',' -> buf.(p) <- int_of_char (input_char stdin); loop (idx+1) p
    | '[' -> loop (if buf.(p)=0 then (rp (idx+1) 0)+1 else idx+1) p
    | ']' -> loop (if buf.(p)<>0 then (lp (idx-1) 0)+1 else idx+1) p
    | '\n' -> ()
    | _   -> raise Unexpected_char
  in loop 0 0
let _ = interpret (read_line()^"\n")
$ ocaml bf.ml
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.
Hello, world!$ 

ゴルフを経験した後だと、 loop を ($) にすれば括弧が消せる!とか、if 式を消したい!とか、while 使った方がいいかも!とか、例外は無視してよくね!とか思ってしまう。

swc にリソースが埋め込めない

ボタンをいくつか縦に並べたメニューをライブラリに移したんだけど、うまく動かなかった。メニューにはそれぞれ違った背景画像を使うんだけど、その画像が表示されない。どうやら埋め込まれていないのが原因らしい。プロジェクトのプロパティで、swc に含めるように指定したはずなんだけど…。

メニュー項目は、背景画像とテキストだけが異なるがその他の設定は共通なので、 Repeater を使って次のように書いた。(関係ない属性は省略した)

<!-- メニューボタンを縦に並べる(画像が埋め込まれない) -->
<mx:Repeater id="menuRepeater" dataProvider="{menuItemArray}">
  <mx:Canvas backgroundImage="{menuRepeater.currentItem.imgpath}">
    <mx:VBox width="100" height="100" click="(略)">
      <mx:Text htmlText="{menuRepeater.currentItem.title}"/>
    </mx:VBox>
  </mx:Canvas>
</mx:Repeater>

メニュー項目を1つずつ書いて背景画像を直接 "@Embed" 指定すると埋め込まれるんだけど、Repeater を使った場合にはその書き方は無理。
ちょっと調べたら、次のように書くと埋め込まれるという記述を発見した。

[Embed(source="assets/menu01.jpg")] [Bindable] private var menu01 : Class;

これも試したけどだめでした。なんでだ!

結局、以下のように画像を使うことを静的にわかるように(?)してやると、無事に埋め込まれました。

[Embed(source="assets/menu01.jpg")] [Bindable] private var menu01 : Class;
[Embed(source="assets/menu02.jpg")] [Bindable] private var menu02 : Class;
[Embed(source="assets/menu03.jpg")] [Bindable] private var menu03 : Class;
[Embed(source="assets/menu04.jpg")] [Bindable] private var menu04 : Class;
[Embed(source="assets/menu05.jpg")] [Bindable] private var menu05 : Class;
[Embed(source="assets/menu06.jpg")] [Bindable] private var menu06 : Class;
[Embed(source="assets/menu07.jpg")] [Bindable] private var menu07 : Class;
private var imgArray : Array = [menu01, menu02, menu03, menu04, menu05, menu06, menu07];
private function selectImg(idx : int) : Class {
  return imgArray[idx];
}
<!-- メニューボタンを縦に並べる(画像が埋め込まれる) -->
<mx:Repeater id="menuRepeater" dataProvider="{menuItemArray}">
  <mx:Canvas backgroundImage="{selectImg(menuRepeater.currentIndex)}">
    <mx:VBox width="100" height="100" click="(略)">
      <mx:Text htmlText="{menuRepeater.currentItem.title}"/>
    </mx:VBox>
  </mx:Canvas>
</mx:Repeater>

バロス!俺はなんか根本的な勘違いをしてるのか!?

ライブラリプロジェクト

FlexBuilder で開発していた某Eラーニングのサイトをシリーズ化することになったので、共通部分をライブラリ化することにした。
FlexBuilder で新しく"ライブラリプロジェクト"ってのを作って、そのライブラリを使うプロジェクトのプロパティから FlexBuilderPath -> LibraryPath -> AddProject で新しく作ったライブラリプロジェクトを指定する。
Eラーニングのサイトは構造が単純なので、共通のクラスやリソースファイルを移動していくだけの簡単なお仕事でした。
いろいろなサイトで書かれているけど、ライブラリプロジェクト内のクラスやリソースファイルはそのままでは外部からアクセスできないので、ライブラリプロジェクトのプロパティの FlexLibraryBuildPath で、ライブラリに含めるものを指定する必要がある。

選択されたテキストを alc で引く Firefox 拡張


予想外に5分でできた。というか選択されたテキストを wikipedia で引くサンプル(http://stupidbob307.eshlook.com/jetpacks/wikipedia.html)があったので、その URL を変えただけなんですけどね。予想以上に便利だったので Opera でも使いたい。(自画自賛)

jetpack.future.import("selection");
jetpack.future.import("slideBar");
jetpack.slideBar.append({
  icon: "http://www.alc.co.jp/favicon.ico",
  width: 350,
  html: '<style>scrollbar * {display: none !important;}</style><iframe class="frame" height="99%" width="100%" src="http://www.alc.co.jp/"></frame>',
  onReady: function(slide) { jetpack.selection.onSelection(function() {
    $(".frame", slide.contentDocument).attr("src", "http://eow.alc.co.jp/" + jetpack.selection.text + "/UTF-8/?ref=sa");
    });
  }
});

alc を別のタブで開いてるだけだから、利用規約的にも大丈夫だと思っている。

Flex 3 アプリケーションの国際化

ポルトガル語やら中国語やらフランス語を使うことになったので、http://d.hatena.ne.jp/kagamihoge/20090425/1240648033 を見ながら Flex の ResourceBundle を使ってみることにする。

ロケールの追加

まず、ディレクトリを以下のような感じで構成し、

project/bin-debug/...
       /html-template/...
       /locale/ja_JP
              /pt_BR
              /zh_CN
       /src/...

コンパイルオプションを以下のように指定してみた。

 -locale=ja_JP,pt_BR -source-path=../locale/{locale} -allow-source-path-overlap=true -use-network=false

ポルトガル語ロケール pt_BR を追加すると、"pt_BR のロケールが見つかりません"みたいなエラーが出るので、FlexBuilder についてくる copylocale コマンドを使ってダミーのロケールを作る。僕の場合にはなぜか copylocale が2つあったのでちょっとはまった。

/usr/local/flexsdk/bin/copylocale
/Applications/Adobe\ Flex\ Builder\ 3/sdks/3.2.0/bin/copylocale <- こっちを使う

ここも参照: http://livedocs.adobe.com/flex/3_jp/html/help.html?content=l10n_3.html

GUI アプリケーションのテスト

アルバイトをやっていて思ったことをつらつらと。
GUI アプリケーションのテストをする際に、どこが難しいのかというと、

  • GUI は、ユーザの指定したレイアウトを記憶することが多い。ウィンドウの表示位置とか、スクロールバーの値とか、2ペインのウィンドウの大きさの比とか。これによりテスト対象のコンポーネントの位置が変化する
  • GUI は表示されていないオブジェクトに対する入力が行えないし、ユーザからの入力系列をキャッシュするバッファという概念もない。そのため、アプリケーションがある特定の状態になってから操作する、というテストをうまく作れない
  • GUI で表示されるデータは、ユーザという高機能なシステムが理解できればよく、他のアプリケーションから再利用される状況は想定していない。つまり、デバッガからうまく扱えるようになっていない

だと思う。expect でテストを一生懸命書く感じになる。

複数のテストを自動で実行する際にどこが難しいのかというと、

  • 「初期化」が難しい
  • 結果の判定が難しい

ことだと思う。どちらも「GUI アプリケーションの状態」がなんなのかよくわかっていないのが問題。

テスト対象アプリケーションにどんな機能があれば解決できるかというと

  • GUI の状態」の定義
  • GUI アプリケーションの状態のスナップショットを取り、いつでもそこに復帰できる機能
  • 特定のイベントが起きるまで待機する機能
  • 座標やコンポーネントの表示名以外の方法で、特定の GUI コンポーネントを普遍的に指定できる機能
  • GUI アプリケーションの状態を他のアプリケーション(デバッガ)から取得できる機能

だと思う。

結論:これらの機能を実現する GUI フレームワークが必要だ!
デバッグモードでビルドすると、任意のタイミングで GUI の状態をシリアライズできるようなもの。ちなみに Plan9 ではプロセスをファイルで表現していて、プロセスファイルシステム以下のファイルにアクセスすることで、アプリケーションの状態を再利用可能な形で(テキストで)取得することができる(たぶん)。ただ、取得できるデータの形式はアプリケーションによる(たぶん)。

10分でコーディング

頑張ってブログを書こうキャンペーン中。理由は特にない。
http://ameblo.jp/programming/entry-10001721422.html のやつ。 then の中はもっと綺麗にかけるんじゃないかと思うんだけど。

deal :: Int -> [Int] -> [[Int]]
deal num cards =
    let (h,t) = splitAt num cards
    in  if length h == num
        then map (\(x,y) -> x:y) $ zip h (deal num t)
        else replicate num []