2013年3月20日水曜日

論文を書こう・自動化のススメ

先週、諸般の事情によって実に3年ぶり(!)に論文を投稿しました。いろいろと時代は変わったわけで、やや出遅れ感はありますが時代にあわせた執筆スタイルにしようとしていろいろ試行錯誤してみました。普段バリバリ論文書いている方々にはむしろ時代遅れの内容のような気がしますが、未来の自分のためにも結果をまとめておきたいと思います。

自動化は正義

自動化できることはなんでも自動化することで、未来の自分を救います。「どうせ一度しかやらないし」と思っているあなた、本当に一度ですむと思っていますか? LaTexのコンパイルは論文提出までに何百回実行しますか? その評価のパラメータ、本当に正しいですか? 実験データは何回更新されますか? むしろ、今まで一度で済んだことがあるんですか? (ということで、一度ですんだことがある人にこの記事は不要です)

自動化のメリットは主に2つだと思っています。1つ目は言わずもがなですが、人間の作業を省力化できること。これは単純に作業時間を短縮できるというだけでなく、いちいち気持ちの切替をしなくていいというメリットがあります。論文執筆では思いの外いろいろな作業をしなければならないので、なるべく大切な作業に集中できるように、機械ができる仕事は機械にやらせるのがよいです。2つ目は各種作業の再現性が高いことです。論文投稿した結果、めでたく(?)条件付き採録で返って来ました。でも評価が足りないからもう少しデータを足してね、という査読者からの指摘があったとき、「あれ、この評価ってどういう手順でやったっけ?」という記憶喪失事件は枚挙に暇がありません。その結果、同じデータを使っているはずなのになんか結果が微妙に違うのはなぜなんだぜ…、と悩んで時間を無為に消費してしまうこともあるあるだと思います(僕だけ?)。

もちろん自動化するということは、それだけ初期投資が高くつくというのは間違いなく、かつ忙しい時にそんなことをしている暇など無い!という考えも、もっともだと思います。ただ、忙しくなるほど手間を省き手順通りに作業を実行してくれる自動化の恩恵が大きくなるのも事実です。最終的には個人のバランス感覚だと思いますが、個人的には今後も積極的に自動化していきたいと思っております。

LaTexからPDFの作成

自作のautobuildというツールを使いました。指定したファイルの更新を検知して、特定のコマンドを走らせるOMakeなどのツールと同じ発想ですが、極力シンプルに動作するものが欲しかったので、ちょろっと作りました。nodejsで動く60行ほどのしょっぼいコードです。(oremakeというツールを参考にさせてもらいました)これを使って*.texや*.epsファイルを指定し、変更があったらplatexなどを自動的に走らせることが可能です。私はMakefileにコマンドをまとめていたので、設定ファイル(build.js)はこんな感じ。同一ディレクトリの*.texファイルとfigsディレクトリの*.epsファイルを指定しています。

{"build_command": "make",
  "build_args":  [],
  "file_list": {".": [".*\\.tex"], 
                "figs": [".*\\.eps"]}
}

執筆はMac上でやっていたので、PDF viewerにはSkimを使っていました。Skimはファイルの変更を検知してファイルの再読み込みをしてくれるので、継続的に自動ビルドされれば勝手に表示を更新してくれます。今まで「ファイル保存 → ターミナルに移動 → makeコマンド実行 → 完了後にPDF viewerを一度閉じる → PDF viewerを再度開く」みたいな行程が必要だったのが、ふと横を見ると常に最新版のPDFが見れるようになります。

テストの実行

皆大好きJenkinsさんの登場です。今回は実装の一部をC++で書いていたので、コンパイルは先程のautobuildというツールを使っていましたが、デバッグ作業をしている時は変更とコンパイルを繰り返さなければならないので、その度にテストが走るというのは少々、いやかなり鬱陶しいわけです。というわけで、今回はJenkins(またのなを高機能crontab)を利用して定期的にテストを実行するようにしました。これはこれでテスト結果がおかしいままだと、際限なく「早く直せよー、おらー」と言うメッセージが出続けるので別の鬱陶しさがありますが、ToDoをせっつかれるという意味では良いのかもしれません。

設定は5分毎にコンパイル実行&テストコード実行で、終了後にPost build taskでスクリプトを実行させました。実行時にテスト結果などのステータスを直接環境変数で渡せないのがちょっと面倒でしたが、HTTPでJenkinsをたたけば結果を返してくれるのでお手軽です。こんな感じのスクリプトを用意して実行していました。テストに失敗するとGrowlが失敗メッセージを表示し、マリオが死んだ時のSEが流れます。(本当はGrowl pluginとJenkins sound pluginで実行すればいいだけなんですが、なんかうまく行かなかったので)


評価の実行

普通、評価は条件をそろえてやるものなので、継続的に結果を出力し続ける必要はないと思うのですが、手順をまとめて実行できるようにしておくことは必要だと考えられます。あくまで個人的な経験ですが、ソフトウェア(あるいはPoCコード的なプロトタイプ)を実装して、その出力をそのまま評価結果として使えるというケースは稀だと思います。だいたいは10段階ぐらいのone liner shell scriptをかませたり、適当なスクリプトを書いたりして結果を正規化、集計することが多いと思います。しかし、往々にして後からどういうone linerを実行したかわからなくなったり、断片的なコードが散乱したりという自体に陥ります(僕だけかもしれませんが)したがって、テストの実行とデータの加工までを一気通貫で実行できるというのがとても便利でオススメです。

特にこれといったツールやフレームワークがあるわけではないですが、データの加工まで一気にやることを考えると、Shell scriptで頑張るのはちょっとしんどい事が多いので、少なくとも一部はPythonやRubyなどお手軽にデータ集計ができるLLを導入するのがよいかと思いますが、そこは個人の趣味で。

グラフの作成

いくつかグラフ作成ツールを試してみましたが、結局某先輩に教えてもらったmatplotlibに落ち着きました。最大のポイントは自分がPythonでデータ加工など大まかな処理はPythonにべったりだったので、習得が容易である&データをそのまま流し込めるという2つのメリットがありました。ggplot2とかも検討したのですが某氏の「ggplot23日経つと使い方忘れるあたりで挫折した」という発言に胸を打たれて諦めました。

評価とグラフ作成は別々のスクリプトでまとめていましたが、Makefileで連続実行できるようにしておくと、make testとか打ち込むだけで、

  1. 自分の実装と関連研究の実装を順次実行
  2. 実行結果の集計
  3. グラフ作成 + epsに出力
  4. epsの変更を検知してPDFを再出力
  5. 最新のPDFを表示

までをフルオートでやってくれるようになります。

さいごに

まとめてみたものの、改めて見返してみるとほとんど当たり前のことばかりでしたが、これで救われる学生さんとかが1人でもいれば嬉しいです。あと「なんという時代遅れ。今時こっちのツールだろうJK」みたいなツッコミがあればぜひこっそり教えてください。

2013年1月6日日曜日

nodejs addon サンプルコード

サーバサイドのJavaScriptエンジンであるnodeで遊ぶことがあったので、addon作成時にまとめたサンプルコードを貼り付けておきます。addonはC++で記述できるので、サンプルとしてまとめたのは

  • 引数+返り値のオーソドックスな関数呼び出し
  • 新しいオブジェクト(連想配列)を返す
  • 処理の途中でコールバックを呼び出す
  • 独自のC++クラスを使う
  • 独自のC++クラスにコールバックを設定して任意のタイミングで呼ぶ
  • libuvで非同期処理させる
です。サンプル本体はgithubにおいてあります。基本的には公式ドキュメントを参考にしているだけなので、必要に応じてそちらも御覧ください。あまり深く検証しているわけではないので、特にご利用は自己責任でお願いします。


#define BUILDING_NODE_EXTENSION
#include <node.h>
#include <unistd.h>
// 趣味。特にnamespaceを切る必要はない
namespace addon {
// ----------------------------------------------------------------------------
// 引数を受け取って、普通に結果を返すだけの関数
v8::Handle<v8::Value> Add(const v8::Arguments& args) {
v8::HandleScope scope;
// 引数はv8::Argumentsにまとめられている
// 引数の個数を見る時はargs.Length ()
if (args.Length() < 2) {
// ThrowExceptionだがここで例外を投げて抜けるわけではない。
// 例外のキューに入るイメージみたい
v8::ThrowException(v8::Exception::TypeError(v8::String::New("Wrong number of arguments")));
// 値を返さない時はUndefinedを返すのがお作法らしい
return scope.Close(v8::Undefined());
}
// 数値や文字列などはC++標準ではなく、それぞれv8独自の型を使う
// 型はわからないので、IsNumber()などを使って型を確認する
if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
// 期待する型じゃなかったらException投げる
v8::ThrowException(v8::Exception::TypeError(v8::String::New("Wrong arguments")));
return scope.Close(v8::Undefined());
}
// 足し算するだけ
// 返す値もv8の型にあわせる
v8::Local<v8::Number> num = v8::Number::New(args[0]->NumberValue() +
args[1]->NumberValue());
return scope.Close(num);
}
// ----------------------------------------------------------------------------
// 新しいオブジェクト(連想配列)を生成して返す
v8::Handle<v8::Value> CreateObject(const v8::Arguments& args) {
v8::HandleScope scope;
// v8::Object::New ()で作成する。
v8::Local<v8::Object> obj = v8::Object::New();
// Setを使う。連想配列のキーはNewSymbolで生成する
obj->Set(v8::String::NewSymbol("msg"), args[0]->ToString());
return scope.Close(obj);
}
// ----------------------------------------------------------------------------
// 処理が終わったらコールバックを呼び出す関数
v8::Handle<v8::Value> RunCallback(const v8::Arguments& args) {
v8::HandleScope scope;
// 本当はちゃんとargs[0]が関数かどうかチェックする必要あり
// v8::Functionにキャストする
v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[0]);
// コールバックに渡す引数を作成する
const unsigned argc = 1;
v8::Local<v8::Value> argv[argc] = {
v8::Local<v8::Value>::New(v8::String::New("hello world"))
};
// コールバック呼び出し
cb->Call(v8::Context::GetCurrent()->Global(), argc, argv);
return scope.Close(v8::Undefined());
}
// ----------------------------------------------------------------------------
// 独自クラスを使う
// node::ObjectWrapを継承する
class Blue : public node::ObjectWrap {
public:
// クラス名や関数名を登録するため、初期化関数を静的に用意しておく
static void init (v8::Handle<v8::Object> target);
private:
Blue () : count_(0) {}
~Blue () {}
// v8から呼び出される関数はすべて静的関数にする
// 引数const v8::Argumentsに固定
static v8::Handle<v8::Value> New (const v8::Arguments &args);
static v8::Handle<v8::Value> Five (const v8::Arguments &args);
static v8::Handle<v8::Value> Count (const v8::Arguments &args);
int count_;
};
// 初期化関数。v8のプロセスで一度しか呼び出されない(はず)
void Blue::init (v8::Handle<v8::Object> target) {
// FunctionTemplateを作成し、自前のオブジェクト生成関数New (newではない)を設定
v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(New);
// クラス名決める
tmpl->SetClassName (v8::String::NewSymbol ("blue"));
tmpl->InstanceTemplate ()->SetInternalFieldCount (1);
// メンバ関数も名前とFunctionで登録
tmpl->PrototypeTemplate ()->Set (v8::String::NewSymbol ("Five"),
v8::FunctionTemplate::New (Five)->GetFunction ());
tmpl->PrototypeTemplate ()->Set (v8::String::NewSymbol ("Count"),
v8::FunctionTemplate::New (Count)->GetFunction ());
// 最後にクラス生成の関数を登録
// 多分、ここのシンボル名とSetClassNameの名前は変えられると思うが、
// 混乱すると思うのであまりオススメできない&未検証
target->Set (v8::String::NewSymbol ("blue"),
v8::Persistent<v8::Function>::New(tmpl->GetFunction ()));
}
// クラス生成関数
v8::Handle<v8::Value> Blue::New (const v8::Arguments &args) {
v8::HandleScope scope;
// ここだけは普通にnew
Blue * obj = new Blue ();
// これを呼び出す必要がある
obj->Wrap (args.This ());
return args.This ();
}
v8::Handle<v8::Value> Blue::Five (const v8::Arguments &args) {
v8::HandleScope scope;
return scope.Close (v8::Number::New (5));
}
v8::Handle<v8::Value> Blue::Count (const v8::Arguments &args){
v8::HandleScope scope;
// node::ObjectWrap::Unwrap <クラス名> (args.This ()) することで、
// インスタンスを取り出すことができる
Blue * ao = node::ObjectWrap::Unwrap <Blue> (args.This ());
// 後は好きにごにょごにょいじる
ao->count_ += 1;
return scope.Close (v8::Number::New (ao->count_));
}
// ----------------------------------------------------------------------------
// 任意のタイミング(関数の終了時などではない)でコールバックを呼び出す
class Orange : public node::ObjectWrap {
public:
static void init (v8::Handle<v8::Object> target);
private:
static v8::Handle<v8::Value> New (const v8::Arguments &args);
static v8::Handle<v8::Value> SetCallback (const v8::Arguments & args);
static v8::Handle<v8::Value> RunCallback (const v8::Arguments & args);
// v8::Persistentでメンバ変数を用意するのが重要
v8::Persistent<v8::Function> callback_;
};
void Orange::init (v8::Handle<v8::Object> target) {
v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(New);
tmpl->SetClassName (v8::String::NewSymbol ("orange"));
tmpl->InstanceTemplate ()->SetInternalFieldCount (1);
tmpl->PrototypeTemplate ()->Set (v8::String::NewSymbol ("set_cb"),
v8::FunctionTemplate::New (SetCallback)->GetFunction ());
tmpl->PrototypeTemplate ()->Set (v8::String::NewSymbol ("run_cb"),
v8::FunctionTemplate::New (RunCallback)->GetFunction ());
target->Set (v8::String::NewSymbol ("orange"),
v8::Persistent<v8::Function>::New(tmpl->GetFunction ()));
}
v8::Handle<v8::Value> Orange::New (const v8::Arguments &args) {
v8::HandleScope scope;
Orange * obj = new Orange ();
obj->Wrap (args.This ());
return args.This ();
}
v8::Handle<v8::Value> Orange::SetCallback (const v8::Arguments & args) {
v8::HandleScope scope;
Orange * orange = node::ObjectWrap::Unwrap <Orange> (args.This ());
if (args.Length () != 1 || !args[0]->IsFunction ()) {
v8::ThrowException (v8::Exception::TypeError (v8::String::New ("Invalid argument")));
return scope.Close (v8::Undefined ());
}
// v8::Persistent<v8::Function>::New でコールバック関数を保持するインスタンスを
// 生成しなければならない
orange->callback_ =
v8::Persistent<v8::Function>::New (v8::Local<v8::Function>::Cast(args[0]));
return scope.Close(v8::Undefined());
}
v8::Handle<v8::Value> Orange::RunCallback (const v8::Arguments & args) {
v8::HandleScope scope;
Orange * orange = node::ObjectWrap::Unwrap <Orange> (args.This ());
v8::Local<v8::Value> argv[1];
argv[0] = v8::String::New ("scar");
// コールバック関数を呼び出す時は、もう一度v8::Localに
v8::Local<v8::Function> cb = v8::Local<v8::Function>::New(orange->callback_);
// コールバック!
cb->Call (v8::Context::GetCurrent ()->Global (), 1, argv);
return scope.Close(v8::Undefined());
}
// ----------------------------------------------------------------------------
// libuvの機能を使った非同期処理
// データをやりとりするためのクラス
class Alice {
public:
v8::Persistent<v8::Function> callback_;
long long res_;
};
// 処理をする関数
void DoTask (uv_work_t *req) {
// 適当になんかやらせる
long long a = 1, p, q;
p = q = a;
for (long long n = 0; n < 10000000000; n++) {
a = p + q;
q = p;
p = a;
}
// req->dataをキャストすると、そのまま受け渡したインスタンスが見える
Alice * k = static_cast<Alice *> (req->data);
k->res_ = a;
}
// 処理終了後の後始末関数
void FinishTask (uv_work_t *req) {
v8::HandleScope scope;
Alice * a = static_cast<Alice *> (req->data);
v8::Handle<v8::Value> argv[1];
// 処理結果を受け取る
argv[0] = v8::Integer::New (a->res_);
v8::TryCatch try_catch;
a->callback_->Call(v8::Context::GetCurrent()->Global(), 1, argv);
// お片づけ
a->callback_.Dispose();
delete a;
delete req;
if (try_catch.HasCaught())
node::FatalException(try_catch);
}
// 処理開始(この関数が呼び出される)
v8::Handle<v8::Value> RunTask (const v8::Arguments& args) {
v8::HandleScope scope;
// uv_work_tを生成
uv_work_t *req = new uv_work_t;
// 結果をやりとりするためのクラスを生成
Alice * a = new Alice ();
// コールバック登録
a->callback_ = v8::Persistent<v8::Function>::New(v8::Local<v8::Function>::Cast(args[0]));
req->data = a;
// uv_queue_work()でキューに入れる。
uv_queue_work(uv_default_loop(), req, DoTask, FinishTask);
return scope.Close (v8::Undefined());
}
// == Initializer ========================================================
void Init(v8::Handle<v8::Object> target) {
// targetに関数のシンボル(JavaScript側から呼び出すときの名前)と関数テンプレート化された
// 関数を入れ込む
target->Set (v8::String::NewSymbol("add"),
v8::FunctionTemplate::New(Add)->GetFunction());
target->Set (v8::String::NewSymbol("createObj"),
v8::FunctionTemplate::New(CreateObject)->GetFunction ());
target->Set (v8::String::NewSymbol("runCallback"),
v8::FunctionTemplate::New(RunCallback)->GetFunction());
target->Set (v8::String::NewSymbol("async_task"),
v8::FunctionTemplate::New(RunTask)->GetFunction());
Blue::init (target);
Orange::init (target);
}
// 初期化。"addon"という名前のアドオンになる
// 二番目の引数は初期化
NODE_MODULE(addon, Init);
}
view raw gistfile1.cpp hosted with ❤ by GitHub

2013年1月5日土曜日

「オンラインゲームを支える技術 --壮大なプレイ空間の舞台裏」読んだ

新年読書大会第二弾。オンラインゲームを支える技術 --壮大なプレイ空間の舞台裏を読みました。別にゲームを作りたいとかではあまりないのですが、オンラインゲームはよくやるので、興味本位で読んでみました。ちなみに、ここではソシャゲのように非同期で連携するものではなく、リアルタイムでユーザ間のインタラクションがあるものが想定されています。

IT技術中級者〜上級者向け


内容としてはオンラインゲームの歴史にはじまり、どのような企画ができるか、インフラ(ネットワーク、計算機)の見積りはどのようにすればいいか、どのようなアーキテクチャが考えられるか、パフォーマンスを維持するにはどうすればいいか、運用はどうすればいいか…、というようなことがかなり広くざっくりと書かれています。「ざっくりと」というのは決して適当に書かれているという意味ではないのですが、それぞれの話の粒度がまちまちな印象でした。新しいゲームを作るための企画段階の話から、具体的なコードはどのように書けるか、というような話まで、かなり雑多に入り交じっている感じでした。

これはどういうことなのかというと、おそらく「君はそれなりにIT技術者として経験積んでいるつもりかもしれないが、オンラインゲーム業界に行こうと思ったらここに書かれていることぐらいは押さえておいてね!」ということなのかと思いました。自分はCSの基礎は嗜む程度にやっていますが、あまりエンジニアよりの職種ではないで、ゲームならではの視点みたいなところで「なるほど」と思うところは多かったです。

先に書いた通り、この本ではリアルタイムでのオンラインゲームを重視しており、最近のソシャゲの隆盛を考えると今後どうなるかよくわかりませんが、その方面に就職・転職を考えている人は一読して、ここでのべられている能力(技術・政治・金勘定)が網羅できているか考えるとよかったりするのでは、とか思ったりします。・・・実際どうかは本職の人に聞いてみたいところですが。

「デザイニング・インターフェース ―パターンによる実践的インタラクションデザイン」読んだ



正月休みを利用して、デザイニング・インターフェース ―パターンによる実践的インタラクションデザインをざーっと読みました。
だいぶ前に買って積み本になり、引越しするときに実家に放置してきたのですが、UIを作る機会がちょこちょこ出てきたのと、実家に帰る&休みができるというイベントが重なったので、一気に走り読みしました。

基本的には事例集


プログラミングにおけるデザインパターンと同じように、UIのデザインをする際にどのような鉄板手法があるか? ということをまとめた本です。数が多いのですが、「グローバルナビゲーション(全頁に共通して定常的に使えるナビをおく)」や「動的なメニュー項目(必要に応じて項目を有効化・無効化・追加・削除)」のように様々なアプリケーションで使われている手法なので、言われてみれば「そりゃそうだね」と容易に理解できるものですが、いざ自分がそれを使おうとしたとき、各機能がどのような特性を持つのか?ということが良くまとまっていました。

おそらく、プロのUIデザイナーの方々は経験則として知っていることばかりなのだと思いますが、UIデザインが本職ではないんだけど、UIデザインをしなければいけなくなった! というような人が、どのような機能をどう組み立てていけばいいのか、この機能はどういう状況なら有効なのかを調べつつ使うのに適しています。本の中には9つのデザインに対する考え方がそれぞれ概論で示され、その後個別の100個弱のパターンが細かく解説されています。よくデザイン解説のWebページなどでは直感的に解説されていることが、(少なくとも自分の知っている範囲では)体系的に説明されているため、わかりやすく、自分がそれを使おうと思った時にも安心感があります。

UIデザインの素人だけどUIを作らねば、という人は手元にあるといいのでは


自分のように特にデザイン能力が高くないんだけどUIを作らないといけない場合、手元1冊において辞書的に使うのがよさそうな良書だと思います。UI(ユーザとインタラクション)がある部分に特化しているので、カッコイイ感じのデザインができるようになるというわけではないんですが、定石がまとまっているので、使いやすいさの向上には大いに役立ちそうです。

2012年12月27日木曜日

HMZ-T2

HMZ-T2を買いました。



それなりにいいお値段する買い物ではありましたが、可処分所得が下がる傾向が見られない今だからこその買い物です! やっぱり最新技術には触れておかないとね。

とりえあず、使用感など。


ゲーム:MMORPG(3D)には少なくとも不向き

試してみたのはWorld of Warcraft、Perfect Worldの2つです。両方共いわゆるMMORPGです。4Gamerのレビューでも書かれていましたが、仮想世界をぼんやりと冒険するのには向いていますが、命を賭けてゲームしているような人には到底おすすめできません。

致命的なのは文字を読むのがそれなりに辛い点です。僕がメガネユーザだからなのかも知れませんが、画面全体に正しく焦点を当てるのにかなり神経を使います。HMZ-T2の機体のほとんどの重量をおでこで支える構造になっているので、目を見開いたりするだけで焦点位置がずれて、あちこちの文字がぼやけます。World of Warcraftは少しやっていましたが、本気でやろうと思うと攻撃ボタンを連打していればいいわけではなく、各種メッセージを目で追いかけながら戦わないといけないので、文字が見にくいというのは大きなハンデキャップになります。これはおそらく、数値などのメッセージをおいかける他のゲームでも同じポイントで悩まされることになると思います。

3DベースなMMORPGの場合、のんびりゲーム内を歩きまわるだけで結構楽しいです。周りの情報が遮断されることによる没入感や、映画を見ているような感覚になるので、別の世界を旅しているような楽しさがあります。ただし3D酔いはかなり注意が必要です。普通のディスプレイで「3D酔いが酷い」と言われるようなゲームでまったく酔ったことがない自分ですら、ちょっと気持ち悪くなりました。首を傾けると世界も一緒に傾くので、首をうまく固定するなどの慣れが必要なのかも知れません。

動画再生:バリバリ動く系の映像は見ていて気持ちがいい

ゲームとの相性の悪さに対して、動画鑑賞にはかなり適したデバイスだと思います。とりあえず手元にあった「エヴァンゲリオン:破」の戦闘シーンと「ハリー・ポッターと賢者の石」のクィディッチのシーンを見てみました。両方共、映画館で見たことがあったので、それに比べると若干劣るものの、46インチの液晶で見るよりはかなり迫力がありました。家庭内で手軽に映画館気分を味わえるのは、なかなか良い感じです。

動画再生で困るかもしれないと思ったのは、食べ物・飲み物をつまんだり飲んだりしながら見るのは、かなり難易度が高そうということです。基本的に見ている映像しか見えないので食べ物・飲み物をつかむのはちょっと大変でしょう。隙間からちょっとは見えますが、こぼしたりして大惨事というのを考えるとちょっと怖いです。したがって、動画を見ることだけに集中する、というのが鑑賞時の前提になりそうですね。

他雑感

とりあえず、まだ慣れていないだけかも知れませんが、長時間の装着はかなり疲れます。単純に目がつかれるということもありますし、おでこに負荷がかかるので、だんだんおでこが痛くなってきます。(mixiの方の実例)T1に比べて軽くなったとはいえ、根本的な解決にはなっていないので、おそらく連続使用は2時間ぐらいが限度なのではないかと思います。映画見るのにはちょうどいいぐらいなのかなぁ、と。

また、MMORPGの件でも書きましたが、文字を読むのは結構辛いです。Head Mount Displayをつけて颯爽とプログラミングするようなことを夢見ている方もいるかもしれませんが、個人的にはお勧めできません。もう少し、科学の発展を待ちましょう。

で、買うべきか否か?

まだ開封後5時間ほどですが、価格とパフォーマンスを考えると万人向けのデバイスではないことは断言できます。視聴デバイスとしてディスプレイほどの汎用性があるわけではなく、同じ値段なら質のいい液晶ディスプレイや大型テレビを買う方がおすすめかも知れません。・・・なので、他の人に買うべき、と主張する強い理由は特に無いのが正直なところですねー。ただ、いろいろなレビューサイトで書かれている通り、これまで散々だったHMDの歴史における大きな一歩なのは間違いないので、今はハードウェアの性能云々というよりは、このデバイスに適したコンテンツがないか、いろいろ探ってみたいところです。





2012年8月18日土曜日

「プログラミング言語 違い」の画像の解釈

@soo_mei プログラミング言語 違い で検索したらこの画像にたどり着いたけど訳がわからない
プログラミング言語 違い で検索したらこの画像にたどり着いたけど訳がわからない... on Twitpic

というのがあって、ちょっとおもしろかったので、自分なりの解釈を書いてみました。なんとなく自分が普段使っているもの中心に書いてます。

注意:この解釈は特定の思想・信条・政治・宗教を貶めたり批判・非難するものではありません。また、各プログラミング言語を批判するものでもありません。ネタとしてお楽しみください。

Assembly: 手術道具

目に付く道具の中でも、もっとも精密な作業ができる。これを使って通常では困難な問題も解決できる・・・、かもしれない。なにしろ基本が職人技なので、熟練の職人が使うとあざやかに病巣を取り除いたりできるかもしれないが、未熟だったりすると逆に悪化する可能性があるので、気軽に使えばいいというものでもない。

C言語: アーミーナイフ

機能は必要最低限しか用意されていないが、それぞれの道具の切れ味は十分なので、訓練された軍人ならこれ一つあれば無人島でのサバイバルも可能。ただしそれぞれの道具はとてもシンプルなので応用力が必要だし、素人にはオススメできない。

C++: リッチアーミーナイフ

もとアーミーナイフから、さらに大量の機能が追加されてリッチになった。やったね、これでできることがたくさん増えたよ! ところが機能の種類が多すぎて、覚えるのも大変だし、どの場面でどの道具を使えばいいのかよくわからない。結局使うのはナイフとハサミなど、一部の機能になってしまう。これならもとのアーミーナイフでもいいのでは…という疑念が頭をよぎるが、いややっぱりちょっと便利なところもあるし、という煮え切らない感じ。

JavaScript: ハサミ

目的特化型で作られているので、ある場面ではとても手軽で使いやすい。持ち方を変えると本来とは違った切り方もできるようになるが、やっぱり専用のナイフなどに比べると使いにくい。

Visual Basic: スプーン

ただのスプーンなので、何かをよそうのには使いやすいが、他の状況への応用はハサミよりよっぽど難しい。だって何か切れるわけでもないし、これで他に何をしろというの…。達人はこれで穴を掘って脱獄とかできるかもしれないが、一般人はそんなことしない。

Java/C#: プラスチックナイフ

とっても簡単に作られているし、刃の部分も安全なので、子供だって安心して手軽に使える。これで危ないナイフなんて使わないでも大丈夫! でももとの作りがそんなに尖っているわけではないので、作るものによっては苦労するかもね。

Perl: 日本刀

かつて戦国時代は歴戦の戦士たちが日本刀を使って数々の戦いを切り抜けてきた。しかし使いこなすためには剣の道を極めなければならず、使い方の流派もいろいろ分かれているため、他の道具が多く使われるようになっていった。今でも愛用している利用者は少なからずいる。

Python: チェーンソー

雑草をなぎ払い、巨木を切り倒す。目的に向かって突き進む力を与えてくれるすばらしい破壊力を持っている。でもちゃんと使いこなすためには、スイッチの入れ方だけじゃなくて注意すべきルールやマナーが結構多い。特に注意が散漫だと(インデントの位置を間違えて)大怪我するので、よいこのみんなは注意して使ってくれ!

Haskell: SF

想像を絶するなにかが起きている。それゆえにいまいちピンとこない。

2011年10月2日日曜日

v8 (Javascript Engine)を触ってみた

今更ってレベルじゃないぜ。

ちょっと思うところあってv8 javascript engine(http://code.google.com/p/v8/)で遊んでみました。公式のドキュメントがだいぶ少ないのですが、いろんな人が2年くらい前に遊んでいたようなので情報は適度に転がっています。

ソースコード
// build) g++ v8_test.cc -o v8_test -lv8
#include <v8.h>
#include <string>
char * js_src =
"function laugh() {"
" print ('HAHAHA'); "
"}; "
"s = append ('echo');"
"print (s);"
"print ('I am ' + obj_test.name); "
"obj_test.func ();";
v8::Handle<v8::Value> PrintArgs (const v8::Arguments &args) {
v8::String::AsciiValue str(args[0]);
printf ("[Print] '%s'\n", *str);
return v8::Undefined ();
}
v8::Handle<v8::Value> PrintTest (const v8::Arguments &args) {
printf ("[Print Test]\n");
return v8::Undefined ();
}
v8::Handle<v8::Value> AppendExclamation (const v8::Arguments &args) {
v8::String::AsciiValue str(args[0]);
std::string s (*str);
s += "!!!";
return v8::String::New (s.c_str (), s.length());
}
int main(int argc, char* argv[]) {
v8::HandleScope handle_scope;
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New ();
// install a original function to global context
global->Set(v8::String::New ("print"), v8::FunctionTemplate::New (PrintArgs));
global->Set(v8::String::New ("append"), v8::FunctionTemplate::New (AppendExclamation));
// install a object to global context
v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New ();
obj->Set ("name", v8::String::New ("DROID"));
obj->Set ("func", v8::FunctionTemplate::New (PrintTest));
global->Set ("obj_test", obj);
// create a source code and compile
v8::Handle<v8::String> source = v8::String::New(js_src);
v8::Handle<v8::Primitive> undef = v8::Undefined ();
v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
v8::Context::Scope context_scope(context);
v8::Handle<v8::Script> script = v8::Script::Compile(source, undef);
v8::Handle<v8::Value> result = script->Run();
context.Dispose();
return 0;
}
view raw v8_test.cc hosted with ❤ by GitHub


実行結果
[Print] 'echo!!!'
[Print] 'I am DROID'
[Print Test]
とりあえず、関数作ってJavaScript側から呼びだすところまでは完成。C++側でJavaScriptの関数を呼び出すところまでやりたいので、後日もうちょっと調べます。

参考文献