vncのアップデートではまる
仕事でcentos入れたPCをvnc経由で使っているが、今日"yum update"してtigetvncをアップデートしたときにvncの挙動がおかしくなったので、そのときの対応をメモ。
動作がおかしくなってからのログを追っていくと、yumで入ったのは
- librsvg2
- tigervnc-server
の2つ(と確かもう一つあった)で、アップデート以降/var/log/messsageにlibrsvg2がエラーを吐いていることから、どうもこいつが怪しい。
調べてみると同じ感じのエラーの記事があって、以下のコマンドでlibrsvg2をダウングレードしたら無事に動くようになった。
# yum downgrade librsvg2
enchant.jsで遊ぶ その2
前回に引き続きenchant.jsで遊ぶ。
今回はもう少しゲームっぽいものってことで簡単なブロック崩しを組んでみた。
main.jsは以下の通り。
/** * enchant.js を使う前に必要な処理。 */ enchant(); GAME_WIDTH = 320; GAME_HEIGHT = 320; GAME_FPS = 15; BALL_PX = 16; BALL_IMG = "image/ball.png"; BLOCK_WIDTH = 64; BLOCK_HEIGHT = 16; BAR_WIDTH = 64; BAR_HEIGHT = 16; SCORE_WIDTH = 80; SCORE_HEIGHT = 16; /** * HTMLのロードが完了したときに実行する関数。初期設定 */ window.onload = function () { /** * ゲームの初期設定 */ game = new Game(GAME_WIDTH, GAME_HEIGHT); game.fps = GAME_FPS; /** * ゲームが始まる前にロードしておくファイルを指定 */ game.preload(BALL_IMG); /** * ロードされたときの関数 */ game.onload = function () { /** * ブロックの作成 */ blockMap = new Array(GAME_WIDTH/BLOCK_WIDTH); for (var i=0; i<blockMap.length; i++) { blockMap[i] = new Array(GAME_HEIGHT/BLOCK_HEIGHT/2); for (var j=0; j<blockMap[i].length; j++) { var red = 0; var green = 0; var blue = 0; if ((i%2 == 0 && j%2== 0) || (i%2 != 0 && j%2 != 0)) { blue = 255; } else { green = 255; } blockMap[i][j] = new Block(i*BLOCK_WIDTH, j*BLOCK_HEIGHT, red, green, blue); game.rootScene.addChild(blockMap[i][j]); } } /** * バーの作成 */ bar = new Bar(GAME_WIDTH/2 - BAR_WIDTH/2, GAME_HEIGHT - 2*BAR_HEIGHT); game.rootScene.addChild(bar); /** * ボールの作成 */ ball = new Ball(GAME_WIDTH/2 - BALL_PX/2, GAME_HEIGHT - 2*BAR_HEIGHT - BALL_PX); game.rootScene.addChild(ball); /** * スコア */ score = new Score(0, GAME_HEIGHT - SCORE_HEIGHT, 0); game.rootScene.addChild(score); /** * 背景色 */ game.rootScene.backgroundColor = 'black'; /** * フレームごとに実行する */ game.rootScene.addEventListener('enterframe', function () { var isChangePos = ball.move(); if (isChangePos) { // 衝突判定 // XXX: 本当は近傍のみ判定すべき for (var i=0; i<blockMap.length; i++) { for (var j=0; j<blockMap[i].length; j++) { if (blockMap[i][j].hp <= 0) continue; if (ball.checkHit(blockMap[i][j])) { var isBreak = blockMap[i][j].hit(); if (isBreak) { score.update(blockMap[i][j].score); game.rootScene.removeChild(blockMap[i][j]); } } } } ball.checkHit(bar); // 境界値判定 ball.borderCheck(); } }); }; game.start(); }; var Ball = enchant.Class.create(enchant.Sprite, { initialize: function(x, y) { enchant.Sprite.call(this, BALL_PX, BALL_PX); this.x = x; this.y = y; this.fx = x * 1.0; this.fy = y * 1.0; this.vx = -4.0; this.vy = -4.0; this.image = game.assets[BALL_IMG]; this.frame = 0; }, /** * ボールの位置に変化があった場合はtrueを返す */ move: function() { this.fx += this.vx; this.fy += this.vy; var isChangePos = false; var nextX = Math.floor(this.fx); var nextY = Math.floor(this.fy); if (nextX != this.x || nextY != this.y) { isChangePos = true; } this.x = nextX; this.y = nextY; return isChangePos; }, /** * 衝突反映 */ checkHit: function(sprite) { var isHit = false; // 移動速度がゲーム内の全サイズの約数であることを暗黙の前提としている if (this.x - sprite.x == -this.width) { if (-this.height <= this.y - sprite.y && this.y - sprite.y <= sprite.height) { this.vx = (this.vx > 0 ? this.vx*-1 : this.vx); isHit = true; } } if (this.x - sprite.x == sprite.width) { if (-this.height <= this.y - sprite.y && this.y - sprite.y <= sprite.height) { this.vx = (this.vx < 0 ? this.vx*-1 : this.vx); isHit = true; } } if (this.y - sprite.y == -this.height) { if (-this.width <= this.x - sprite.x && this.x - sprite.x <= sprite.width) { this.vy = (this.vy > 0 ? this.vy*-1 : this.vy); isHit = true; } } if (this.y - sprite.y == sprite.height) { if (-this.width <= this.x - sprite.x && this.x - sprite.x <= sprite.width) { this.vy = (this.vy < 0 ? this.vy*-1 : this.vy); isHit = true; } } return isHit; }, /** * 境界値判定 */ borderCheck: function() { if (this.y <= 0) this.vy *= -1; if (this.x <= 0) this.vx *= -1; if (this.x >= GAME_WIDTH - BALL_PX) this.vx *= -1; } }); var Block = enchant.Class.create(enchant.Sprite, { initialize: function(x, y, r, g, b) { enchant.Sprite.call(this, BLOCK_WIDTH, BLOCK_HEIGHT); this.x = x; this.y = y; this.MAX_HP = 3; this.hp = this.MAX_HP; this.orgR = r; this.orgG = g; this.orgB = b; this.backgroundColor = this.getRgb(); this.score = 100; }, getRgb: function() { var hpRatio = this.hp * 1.0 / this.MAX_HP; return 'rgb(' + (Math.floor(this.orgR * hpRatio)).toString() + ',' + (Math.floor(this.orgG * hpRatio)).toString() + ',' + (Math.floor(this.orgB * hpRatio)).toString() + ')'; }, /** * 破壊されたらtrue */ hit: function() { this.hp -= 1; this.backgroundColor = this.getRgb(); if (this.hp <= 0) return true; else return false; } }); var Bar = enchant.Class.create(enchant.Sprite, { initialize: function(x, y) { enchant.Sprite.call(this, BAR_WIDTH, BAR_HEIGHT); this.x = x; this.y = y; this.backgroundColor = 'gray'; /** * タッチしている座標とバーの位置を同期させる */ this.addEventListener('touchmove', function(e) { var nextX = e.x - BAR_WIDTH/2; if (nextX < 0) this.x = 0; else if (nextX > GAME_WIDTH-BAR_WIDTH) this.x = GAME_WIDTH-BAR_WIDTH; else this.x = nextX; }); } }); var Score = enchant.Class.create(enchant.Label, { initialize: function(x, y, score) { enchant.Label.call(this); this.x = x; this.y = y; this.color = 'white'; this.score = score; this.text = 'SCORE: ' + this.score.toString(); }, update: function(addScore) { this.score += addScore; this.text = 'SCORE: ' + this.score.toString(); } });
衝突判定で地味に詰まって時間がかかった。前回に比べるとコーディング自体はスムーズ。
# いろいろ動作が怪しいところがまだあるけど
なお、上記のコードだけだと画像とか足りなくて動かないので注意。あくまでメモ書きなので。
複数の画像を描画するとき、どれが先頭に来るかとかはどう制御するのかね。
sceneへの登録順とかかな。
enchant.jsで遊ぶ
ゲームをサクッと作る場合にどういう選択がいいのか最近考えてた。個人的にはiOSとかAndroidとかスマフォとかでも動くように作れると幸せ。
調べてみると、enchant.jsを使ってHTML5+javascriptなゲームを気軽に作れるみたいなので試してみた。
javascriptは書いたことないけど、Unityとかでも使えるし、最近良く見かける言語だからこれを機会に覚えてみたほうがよさそう。
enchant.jsは以下のサイトから入手できる。
http://enchantjs.com/ja/
試しにライフゲームを書いてみた。ライフゲームのルールはウィキペディア参照。
http://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B2%E3%83%BC%E3%83%A0
ゲームの核となるmain.jsは以下の通り。
/** * enchant.js を使う前に必要な処理。 */ enchant(); GAME_WIDTH = 640; GAME_HEIGHT = 640; GAME_FPS = 3; CELL_PX = 8; ALIVE_RATIO = 0.3; /** * HTMLのロードが完了したときに実行する関数。初期設定 */ window.onload = function () { /** * ゲームの初期設定 */ game = new Game(GAME_WIDTH, GAME_HEIGHT); game.fps = GAME_FPS; /** * ロードされたときの関数 */ game.onload = function () { /** * セルの作成 */ cellMap = new Array(GAME_WIDTH/CELL_PX + 2); nextCellStateMap = new Boolean(GAME_WIDTH/CELL_PX + 2); for (var i=0; i<cellMap.length; i++) { cellMap[i] = new Array(GAME_HEIGHT/CELL_PX + 2); nextCellStateMap[i] = new Boolean(GAME_WIDTH/CELL_PX + 2); for (var j=0; j<cellMap[i].length; j++) { cellMap[i][j] = new Cell((i-1)*CELL_PX, (j-1)*CELL_PX); nextCellStateMap[i][i] = false; // Sceneにセルを登録する game.rootScene.addChild(cellMap[i][j]); } } /** * セルの生死を初期化する */ for (var i=1; i<cellMap.length-1; i++) { for (var j=1; j<cellMap[i].length-1; j++) { var rand = Math.random(); if (rand < ALIVE_RATIO) { cellMap[i][j].update(true); nextCellStateMap[i][j] = true; } } } game.rootScene.backgroundColor = 'white'; /** * フレームごとに実行する */ game.rootScene.addEventListener('enterframe', function () { /** * セルの次状態を確認 */ for (var i=1; i<cellMap.length-1; i++) { for (var j=1; j<cellMap[i].length-1; j++) { // 隣接セルの生死を確認 var aliveCellNum = 0; for (var dx=-1; dx<=1; dx++) { for (var dy=-1; dy<=1; dy++) { if (dx == 0 && dy == 0) continue; if (cellMap[i+dx][j+dy].isAlive) { aliveCellNum += 1; } } } if (!cellMap[i][j].isAlive && aliveCellNum == 3) { // 誕生 nextCellStateMap[i][j] = true; } else if (cellMap[i][j].isAlive && (aliveCellNum == 2 || aliveCellNum == 3)) { // 生存 nextCellStateMap[i][j] = true; } else if (cellMap[i][j].isAlive && (aliveCellNum == 0 || aliveCellNum == 1)) { // 過疎 nextCellStateMap[i][j] = false; } else if (cellMap[i][j].isAlive && (aliveCellNum >= 4)) { // 過密 nextCellStateMap[i][j] = false; } } } /** * セルの状態を反映 */ for (var i=1; i<cellMap.length-1; i++) { for (var j=1; j<cellMap[i].length-1; j++) { cellMap[i][j].update(nextCellStateMap[i][j]); } } }); }; game.start(); }; /** * ライフゲームにおける各シェル */ var Cell = enchant.Class.create(enchant.Sprite, { /** * セルを作成する */ initialize: function (x, y) { enchant.Sprite.call(this, CELL_PX, CELL_PX); this.isAlive = false; this.x = x; this.y = y; this.backgroundColor = 'white'; }, /** * 状態更新 */ update: function (isAlive) { this.isAlive = isAlive; if (this.isAlive) { this.backgroundColor = 'black'; } else { this.backgroundColor = 'white'; } } });
動かすにはindex.htmlが必要。enchant.jsのbeginnerのサンプルのindex.htmlがそのまま使えるので、動かす場合は2つを同じフォルダに突っ込んで、enchant.jsのパスだけ変更すればおk。
ソースを見ると、ゲームのSceneにSpriteとかのNodeをぶら下げていくことで一つの画面を作ってるみたい。また、各種Nodeの動作はListenerを使って定義する。
ちなみに動かすとこんな感じ(ブラウザから一部抜き出し、静止画ですが)。
ソース自体は簡単にかけたけど、コード上にエラーがいろいろあって動かすまでに苦労した。javascriptの文法を全然理解してないで書いてるからだけど、javascriptの文法ミスとか変数ミスとかがっつり指摘してくれるようなエディタないかな。Eclipseとか?
pythonのテンプレートエンジンmakoを使ってみる
公式サイトは以下の通り。
http://www.makotemplates.org/
インストール
とりあえず手元の環境にインストールしてみる。
rubyのgemとかにあたるpythonのpipでインストールできるらしいので、とりあえずpipを導入することからはじめる。
まずはeasy_installをインストールを・・・と思ったらすでに手元の環境にはあったので飛ばす。
入れるならソースをサイトから持ってきてez_setup.pyとかを叩けばいいのかな?
pipをインストール。
$ sudo easy_install pip
...
動作確認。
$ pip --version
pip 1.1 from /Library/Python/2.7/site-packages/pip-1.1-py2.7.egg (python 2.7)
pipの使い方は以下のサイトが参考になりそう。
http://d.hatena.ne.jp/rudi/20110107/1294409385
makoをインストール。
$ pip search mako <- 念のため
$ sudo pip install mako
使い方
とりあえず使ってみる。
#!/usr/bin/env python # -*- coding:utf-8 -*- # # test_mako.py # from mako.template import Template from mako.lookup import TemplateLookup def test_text(): myTemplate = Template("Hello! My name is ${name}!") print myTemplate.render(name = "hoge") print "" def test_file(): """ $ cat templates/test_file.txt Good morning! My name is ${name}! """ myTemplate = Template(filename = "templates/test_file.txt") print myTemplate.render(name = "fuga") def test_lookup(): myLookup = TemplateLookup(directories = ["templates"]) """ $ cat templates/test_lookup.txt Goo after noon! My name is ${name}! """ myTemplate = myLookup.get_template("test_lookup.txt") print myTemplate.render(name = "puni") def main(): print "--- test_text ---" test_text() print "--- test_file ---" test_file() print "--- test_lookup ---" test_lookup() main()
$ python test_mako.py
--- test_text ---
Hello! My name is hoge!--- test_file ---
Good morning! My name is fuga!--- test_lookup ---
Good after noon! My name is puni!
上記の例は最も単純な"テンプレートファイル内の変数を置き換える"という例。
string指定だったり、ファイル指定だったり、ディレクトリの中からlookupしたりしてるけど、
基本的にやってることは同じ。
テンプレートファイルの中にループとかpythonのコードとかを書くこともできる。
## -*- coding:utf-8 -*-
##
## test_inline.txt
##
## コメント<%doc>
複数行のコメント
タグ末尾の"\"は改行を抑止するためのもの%doc>\
\
\
## 埋め込み
変数の展開 : name = ${name}
pythonのコードも埋め込める : name.upper() = ${name.upper()}
\## pythonのコード<%
space_name = " %s " % name
%>\
filterの利用 : trim_space_name = "${space_name | trim}"
\## templateが作成されるタイミングでコードを実行した場合は"<% -- %>"の代わりに"<%! -- %>"を使う?<%!
import math
sin_30 = math.sin(math.pi/6)
%>\
sin(30) = ${sin_30}
\## 制御構文
% if name == "hoge":
hogehogehogehoge!!!!
% else:
who are you?
% endif
% for i in range(10):
% if i < 5:
${i} is smaller than five
% elif i > 5:
${i} is larger than five
% else:
${i} is five
% endif
% endfor
\## 関数の定義<%def name="checkNullpo(text)">\
% if text == "nullpo":
ga!\
% else:
it is not nullpo\
% endif%def>\
定義した関数の利用 : ${checkNullpo("nullpo")}
filterとしても使える : nullpo = ${name | checkNullpo}
\## pythonのコードとして関数定義してもOK<%
def checkNullpo2(text):
if text == "nullpo":
return "ga!"
else:
return "it is not nullpo"
%>\
pythonコードで定義した関数の利用 : ${checkNullpo2("nullpo")}
こっちもfilterとして使える : nullpo = ${name | checkNullpo2}
#!/usr/bin/env python # -*- coding:utf-8 -*- # # test_mako_inline.py # from mako.template import Template from mako import exceptions def main(): try: myTemplate = Template(filename = "templates/test_inline.txt") print myTemplate.render(name = "hoge") except: print exceptions.text_error_template().render() main()
$ python test_mako_inline.py
変数の展開 : name = hoge
pythonのコードも埋め込める : name.upper() = HOGEfilterの利用 : trim_space_name = "hoge"
sin(30) = 0.5
hogehogehogehoge!!!!
0 is smaller than five
1 is smaller than five
2 is smaller than five
3 is smaller than five
4 is smaller than five
5 is five
6 is larger than five
7 is larger than five
8 is larger than five
9 is larger than five定義した関数の利用 : ga!
filterとしても使える : nullpo = it is not nullpopythonコードで定義した関数の利用 : ga!
こっちもfilterとして使える : nullpo = it is not nullpo
ところどころに"\"が入っているのは、余計な改行を排除するため。
ただ、最後の改行が消せてなかったりとか改行がどこで入るかの仕様がよくわからん。
まとめると
- テンプレートファイルを用意
- pythonのコードから必要な変数をrenderの引数に渡して実行
という流れでいいのかな?
renderの引数はxmlとか別の形式で用意して読み込ませるとかするといいかも。
pythonで簡易コンソールを作る
pythonで簡易コンソールを作る機会があったのでメモしておく。
コンソールを作る場合、以下のように標準入力でループさせて処理させる方法がある。
参考:http://python.g.hatena.ne.jp/mhrs/20060522/p2
#!/usr/bin/env python # -*- coding:utf-8 -*- # # easy_console.py # import sys sys.stdout.write(">> ") for line in iter(sys.stdin.readline, ""): if line.strip() != "": sys.stdout.write(line) sys.stdout.write(">> ") sys.stdout.write("\n")
$ python easy_console.py
>>
>> hoge
hoge
>> fuga
fuga
>> ^D
一方で、pythonには標準でコンソールがあり、それをうまく流用できないか考えてみる。
調べてみると、コンソールをエミュレートするcodeというモジュールがあるらしい。
http://www.python.jp/doc/release/library/code.html
codeでは
- InteractiveInterpreter : python構文解析とインタプリタ状態を担当
- InteractiveConsole : コンソールエミュレータ (InteractiveInterpreterの子クラス)
の2つが定義されている。
例えば、以下の様なコードを書くとコンソールもどきが起動できる。
#!/usr/bin/env python # -*- coding:utf-8 -*- # # code_console.py # import code console = code.InteractiveConsole() console.interact()
$ python codo_consoele.py
Python 2.7.1 (r271:86832, Jun 25 2011, 05:09:01)
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 1 + 1 * 2
3
>>> for i in range(3):
... print i
...
0
1
2
>>> ^D
デフォルトではpythonのインタプリタとして動作する。これは、InteractiveConsoleのpushメソッドでInteractiveInterpretarに入力した文字列を渡しているから。
pushをオーバライドすることで独自処理が実装できる。
#!/usr/bin/env python # -*- coding:utf-8 -*- # # my_console.py # import sys import code class MyConsole(code.InteractiveConsole): def __init__(self, local=None, filename="<console>"): code.InteractiveConsole.__init__(self, local, filename) def push(self, line): if line == "nullpo": print "ga!!!" my_console = MyConsole() sys.ps1 = "(test)>> " sys.ps2 = "------>> " my_console.interact("### welcome to my console!!! ###")
$ python my_console.py
### welcome to my console!!! ###
(test)>> hoge
(test)>> nullpo
ga!!!
(test)>> ^D
ところで、InteractiveConsoleをそのまま使うと上下とかでコマンド履歴を呼び出す機能がない。
pythonの標準コンソールでは出来るので、"python -vvv"とかして調べてたら以下のドキュメントを見つけた。
http://www.python.jp/doc/release/library/readline.html
早速拡張してみる。
#!/usr/bin/env python # -*- coding:utf-8 -*- # # my_console.py # import sys import code import readline import atexit import os class MyConsole(code.InteractiveConsole): def __init__(self, local=None, filename="<console>", histfile=os.path.expanduser("~/.console-history")): code.InteractiveConsole.__init__(self, local, filename) self.init_history(histfile) def init_history(self, histfile): readline.parse_and_bind("tab: complete") if hasattr(readline, "read_history_file"): try: readline.read_history_file(histfile) except IOError: pass atexit.register(self.save_history, histfile) def save_history(self, histfile): readline.write_history_file(histfile) def push(self, line): if line == "nullpo": print "ga!!!" my_console = MyConsole() sys.ps1 = "(test)>> " sys.ps2 = "------>> " my_console.interact("### welcome to my console!!! ###")
これで上下での履歴の呼び出しができるようになる。
boostメモ (format)
boost/format.hppは指定したフォーマットに従って文字列を生成するときに使える。
sprintfに近い印象だけど、char*とか出てこないから安全?
http://boost.cppll.jp/HEAD/libs/format/doc/format.html
なお、実行時に変な変換をすると例外を投げるので、死んだら困る業務プログラムとかだとちゃんと例外処理しないと痛い目を見ます。
### 追記 ###
"%1%"みたいに場所だけ指定して型を明示的に指定しない方法もある。
以下、サンプル。
/* -*- coding:utf-8 -*- */ /** * format.cpp */ #include <string> #include <exception> #include <boost/format.hpp> int main() { std::cout << boost::format("%d + %d = %d") % 1 % 2 % 3 << std::endl; // stringに突っ込む場合はstrメソッドで変換が必要 // std::string str = boost::format("%s%02d") % "hoge" % 1; std::string str = (boost::format("%s%02d") % "hoge" % 1).str(); std::cout << "format_str = \"" << str << "\"" << std::endl; // 変換に失敗すると例外をあげるため、エラー時も継続して処理を続けるには適切にエラー処理が必要 try { std::cout << boost::format("%s%02d") % "fuga"<< std::endl; } catch ( const std::exception& ex ) { std::cout << ex.what() << std::endl; } /* * 追記 */ // 型を明示的に指定しないでも使える std::cout << boost::format("%1% %2% %3%") % "puni" % 2012 % 3.1415 << std::endl; return 0; }
$ g++ format.cpp
$ ./a.out
1 + 2 = 3
format_str = "hoge01"
boost::too_few_args: format-string referred to more arguments than were passed
puni 2012 3.1415
### 追記 ###
明示的に型を指定しない方法について、明示的に型を指定する場合との速度の違いが個人的に気になったので、簡単な時間計測のコードを書いてみた。
# 比較方法がテキトーなのは使用です。
比較では明示的に型を指定する場合と指定しない場合の双方について、それぞれ1,10,100,1000,10000回連続で実行した場合の実行時間を計測。各パターンはそれぞれ100回実行し、その平均値を実行時間とする。
なお、実行環境はMacBookAirで「1.6GHz Intel Core i5」。
/* -*- coding:utf-8 -*- */ /** * format_2.cpp */ #include <cmath> #include <cstdlib> #include <ctime> #include <iostream> #include <boost/format.hpp> int main() { double msec_explicit, msec_implicit; clock_t begin, end; std::cout << "count\texplicit(msec)\timplicit(msec)" << std::endl; for (int i=0; i<5; i++) { uint64_t count = static_cast<uint64_t>(pow(10, i)); // 明示的に型を指定した場合 msec_explicit = 0.0; for (int j=0; j<100; j++) { begin = clock(); srand(j); for (uint64_t k=0; k<count; k++) { boost::format("%d") % rand(); } end = clock(); msec_explicit += end - begin; } msec_explicit /= 100; // msecに修正 msec_explicit = msec_explicit * 1000 / CLOCKS_PER_SEC; // 暗黙的に型を指定した場 msec_implicit = 0.0; for (int j=0; j<100; j++) { begin = clock(); srand(j); for (uint64_t k=0; k<count; k++) { boost::format("%1%") % rand(); } end = clock(); msec_implicit += end - begin; } msec_implicit /= 100; // msecに修正 msec_implicit = msec_implicit * 1000 / CLOCKS_PER_SEC; std::cout << boost::format("%1%\t%2%\t\t%3%") % count % msec_explicit % msec_implicit << std::endl; } return 0; }
$ g++ format_2.cpp
$ ./a.out
count explicit(msec) implicit(msec)
1 0.01472 0.01152
10 0.11472 0.07488
100 0.66532 0.61853
1000 6.38518 6.08243
10000 63.013 61.4699
結論:ほとんど差はない。
# 比較方法が悪い、実行回数が少ない、など色々問題ありですが
明示的に型を指定しない方が若干速く見えるけど、これは実行順序の影響とかを受けてる気がする。
boostメモ (foreach)
boost/foreach.hppはイテレータを用いたコンテナの順次参照をより直感的に記述できるようにするもの。
http://www.kmonos.net/alang/boost/classes/foreach.html
Javaとかである
for(Hoge hoge : HogeList) {}
のような記法を可能にする。C++0xだとfor文の記法としてJavaの方法が機能拡張されてるみたい。
http://d.hatena.ne.jp/faith_and_brave/20070920/1190285157
なお、BOOST_FOREACHは下記のようにマクロとして定義されているので、中に入れるコード上の文字列が影響するみたい。
コンテナの要素の記述にテンプレートの"<>"がでてくるような場合は予めtypedefしないとだめ。
#define BOOST_FOREACH(VAR, COL) \ BOOST_FOREACH_PREAMBLE() \ if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) {} else \ if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else \ if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) {} else \ for (bool BOOST_FOREACH_ID(_foreach_continue) = true; \ BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL); \ BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0) \ if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) {} else \ for (VAR = BOOST_FOREACH_DEREF(COL); !BOOST_FOREACH_ID(_foreach_continue); BOOST_FOREACH_ID(_foreach_continue) = true)
以下、サンプル。
/* -*- coding:utf-8 -*- */ /** * foreach.hpp */ #include <iostream> #include <string> #include <vector> #include <map> #include <boost/foreach.hpp> int main() { std::vector<std::string> strVec; strVec.push_back("one"); strVec.push_back("two"); strVec.push_back("three"); std::cout << "--- strVec ---" << std::endl; BOOST_FOREACH ( const std::string& str, strVec) { std::cout << str << std::endl; } std::cout << std::endl; std::map<int, std::string> intStrMap; intStrMap[1] = "one"; intStrMap[2] = "two"; intStrMap[3] ="three"; std::cout << "--- intStrMap ---" << std::endl; // // この書き方ではエラーになる // BOOST_FOREACH ( const std::map<int, std::string>::value_type& intStr, intStrMap) { // std::cout << intStr.first << " => " << intStr.second << std::endl; // } // // <>を含むような場合はtypedefする typedef std::map<int, std::string> IntStrMap; BOOST_FOREACH ( const IntStrMap::value_type& intStr, intStrMap) { std::cout << intStr.first << " => " << intStr.second << std::endl; } return 0; }
$ g++ foreach.cpp
$ ./a.out
--- strVec ---
one
two
three--- intStrMap ---
1 => one
2 => two
3 => three