udon's blog

思いついたことを、思いついた時に。忘れないように。

アジャイル早い安い論

http://www.nikkei.com/article/DGXNASFK1402E_U0A211C1000000/

全部は読めてないけどすげーモヤモヤ。。。

ので、何となく思ったこと書いてみます。

安くていいの?

「なるほどなるほど。開発を早く、安くやりたいわけですね。わかりました。それでは完璧な解決方法をお教えしましょう。それは、"開発しないこと"です。」

んーーーん。んーーーーん。

そんなまさか

うちの会社の営業さんに話を聞いても、「アジャイルでやりたいという話しを聞く」らしいんですね。で、前に一回「"では御社として何ができたらアジャイルとお考えですか?"と聞いてくれ」と言ったんです。

んで結局そのお客さんの話を聞いていくと「早く・安く」にたどり着くみたいです。

そんなまさか2

なんか、そんなことが(最大の)問題になるはずないでしょ?って思うのですよ。特に日本のサラリーマンでは。

だって、開発が少々遅れようが、費用が高くつこうが、何か直接的な不利益ありますか? *1

「怒られる?」あ、そう。

じゃぁ

「早くて安ければ、それで万事解決ですか?」という質問も付け加えてみるようにお願いしてみよう。きっと、「yes!!!」なんて言わないはずだ。

*1:極端な例はあるでしょうけども

TDDBC大阪3.0 #tddbc

まだまだ #夜のbootcampで盛り上がってるようですが、熱の冷めやらないうちに振り返っておきましょう *1

今回も前回の岡山同様TA (Teキトーなことを言って Aシを引っ張る)として参加出来ました。

Keep

  • TAした(c/c++)
  • 思いの外gtestの知識が深まった
  • gtest用vim環境が更に整った
  • 前回よりエラソーに喋れた

Problem

  • ペアのコードをあまりちゃんと見れてなかった
  • 三人のペアってむずいな・・・
  • なんでVisual Studio ??

Try

  • もっとgtestの機能を使おう(パラメタライズ、例外補足 etc.)
  • 一回rubyで参加したい
  • ちゃんとペアのコードを見て具体的なhintを出せるように
  • もっとペアの雰囲気を良くしたい
    • アイスブレイク足りてない
      • 顔が怖い
      • 一緒にお昼食べたり
      • 笑いが足りてなかったか
  • 今回の成果を弊社内に展開する

で?

一言「楽しかった。」これに尽きますね。

本来S/W開発って楽しいものなはずなんですよ。「作ったものが動く」。これってすごいことだと思うんです。もちろんエンジニアのマスターベーションで終わっていいわけではないです。けど、言葉は陳腐ですけど「夢がある」はずなんですね。うん。

ただ契約やら妙なプロセスとか腐ったナントカやらででその楽しさが摩耗するわけですね。

TDDって、そのあたりを再確認するのにもいいかなと思うんです。だって、始めるコストってほぼゼロでしょ?今すぐやればいいんですよ。*2

「うわー今日オレ仕事したわー」ていう日って、思い返すとテスト用の環境を整えた日だったりするわけです。「これからナンボでもいけるぜー」ってやつですね。楽しいでしょ?ワクワクしませんか?そうなるとね。

これから

もっとドヤ顔しようぜーーー

自分のプロダクトに自信持とうぜーーー

ってとこですかね。

*1:二日目参加できないし

*2:どれほどか、は別の議論

2012年をふりかえっておく

ちょっと遅れたけど、ざっとふりかえる。

やったこと

keepの前にまず何をやったっけ?と。

参加した勉強会とか

ここに書けたり書けなかったりなのでとりあえずatndでも見てみる。

http://atnd.org/users/62183#join

RxTStudyはコンスタントに参加してますね。あとTDDBCもTA含めて初参加でした。 あと間違えてHTMLの勉強会にも出てますね。まぁこれはこれで楽しかったけど。

そういえば去年の夏頃?からか、「日付かぶって行けないわー」が増えたような印象でした。 もっと増えると楽しくなりそうですねー。

仕事とか

そういえば弊社の人間とちゃんと一緒に仕事するの初めてでしたね。かなり刺激になりました。

その他

で、KPT

じゃぁざっと思い返した辺りでいってみましょう。

keep

  • 積極的に勉強会に参加できた
  • 勉強会を主催した
    • 会社の会議室を使えるようにした
  • 朝会・ふりかえりを弊社社員と実践できた
  • vim力が大幅に上達した *1
    • プラグインをチラッと書けるくらいに
  • mac(book air)を購入した
    • 勉強会とかプログラミングとか捗る
  • (ほんの少し)中四国にリーチできた
    • TDDBC岡山TA
  • 英語の読解力が上がった *2
    • 英語サイトで調べ物できるくらいに

problem

  • 興味が発散して疎かになるところが出てきた
  • 得たものをあまり弊社内に還元できていない
  • 買った本を全然読めてない
  • 呑み過ぎ・体重増加
  • 勉強会エントリを毎回書いてたけど途中でだれた
    • 参加数増えたりもある
    • もう少し書きやすい環境も?
      • markdownで大分マシにはなったけど。。。

try

  • 自分の興味・やりたいことを整理する
    • マインドマップは書くには書いている
      • 常に意識して随時ブラッシュアップできるようにする
  • アジャイル的なプラクティスのトライ
    • 来年からちょっと弊社の人が増えそう?な予感。
      • 今やっていることは最低限継続
      • ちゃんと朝会とかやれたらいいけど。
    • ふりかえりを業務後にやってるので業務内でやる
  • 本棚を工夫してみる?
    • 読むべき!が分かるような・・・。
  • 勉強会情報等弊社内にもっと展開してみる
    • 釣れそう?ならさらに。
  • ダイエットェ。。。
  • 貯金
    • まぁいろいろありましてね。
  • 香川の勉強会に参加する
    • この前のがしてしまったりしてたので

まぁこんなとこかな。

*1:vundle使い出したのが大きかった気がする

*2:業務で触れるドキュメントがどれも英語だった、とかかな

北斗の拳に学ぶ銀の弾丸

いやー思いつきだけで書いちゃったわー。タイトルまでパクリだわー。ちょっと後悔してるけど書いちゃったし公開してみるわー。

はじめに。

まず初めに一つお話しなければなりません。

vimです。

ちがいます。「銀の弾丸」です。

これだけ「名前つけ重要」と言われる業界にあって、この言葉は微妙な位置にないかと思っているわけです。なぜなら「銀の弾丸などない」という言葉だけを見て、意味がわかりますか?ワタシはわかりませんでした。

わかりにくい要因として一番に考えられるのは

wikipedia

銀の弾とは、銀で作られた弾丸であり、西洋の信仰において狼人間、悪魔を撃退する際に用いるものとされていた。

こういった宗教的な知識を前提にしていることだと思うわけです。多くの日本人は西洋的な宗教観には疎いでのはないでしょうか。

まぁ一応それから多少本を読んだりして、今は一応言葉の意味合いは理解したつもりです。が、例えばそれを知らない方にうまく伝える方法はないものか、と考えたわけです。

みんな知ってる

そうです。「多くの人がよく知らないもの」が前提にあるからわかりにくいのです。つまり、みんなよく知ってるものを挙げればいいわけです。

みんなが知っているもの。そう、

vimです。

違います。稀代のギャグマンガ北斗の拳」です。

後半に重きを

言うまでも無いかもしれませんが、簡単に北斗の拳の流れをおさらいすると、

  1. 南斗しばく
  2. 兄弟弟子間の内輪もめ
  3. 変な島
  4. 兄弟子のお子さんのお守り
  5. そして伝説へ

となるわけですが、ここで注目すべきは「変な島」もとい、「修羅の国編」です。

実はそれまでに登場する流派は「南斗」と「北斗」だけでしたが、この修羅の国編で「北斗」にも分派が存在することが明らかになるわけです。*1

そしてこの「北斗の分派」の特徴が、まさに銀の弾丸について考えるにふさわしい教材であると、そう思った次第なのです。

と、前置きが長くなってしまいましたが、具体的な説明を始めるとしましょう。

北斗の分派

修羅の国編で最も印象に残るのは「北斗琉拳」の存在です。ラオウの兄であるカイオウが習得しているのはもちろん、ハン、ヒョウ、シャチ、その師ジュウケイと、どこかで見たような構成になっています。

さらにもうひとつ、重要な武術が登場します。それは「北斗宗家の拳」です。

北斗宗家

その昔、強力な武術を用いて世を統べていた(かどうかは忘れましたが)一派が存在していました。

その一派の名を「北斗宗家」といいます。

彼らは「北斗宗家の拳」と呼ばれる強力無比な武術を用いて最強を誇っておりました。がしかし、長い月日が経つに連れ、この北斗宗家の拳を上回る力が台頭してきます。

それは「さらに強い武術」ではなく、「完璧な受け身の技術」でした。これにより北斗宗家の拳は事実上の戦闘力を失ってしまいます。

さらなる力を

「力がほしいか・・?欲しいならば・・・くれてや」何でもありません。

そうなっては北斗宗家も黙ってはいません。さらなる新しい武術の開発に取り組みます。

そこで選ばれたのが、シュケンとリュウオウです。二人は苦労の末、それぞれ「北斗神拳」と「北斗琉拳」を完成させます。 そう、実は北斗神拳北斗琉拳も同じ北斗宗家の拳を源流に持つ武術だったわけです。

特徴

双方の特徴をおさらいしておきましょう。

北斗神拳

  • 経絡秘孔を突き、内部から破壊する
  • 悲しみによる強さの増強
  • 毒効かない(のはケンシロウの特徴)
  • 複数の奥義(理論上無限大)

北斗琉拳

  • 経絡破孔を突く事で、外部から破壊する
  • 体得した者の体には魔闘気が現れる
  • 相手の周りの空間を歪めてからの一撃による唯一無二の奥義

いろいろあるわけですが、最も重要なのは「奥義」です。

北斗琉拳の奥義は、言葉ではわかりにくいかもしれません。ですのでこちらの画像をご覧ください。

空間を歪める

破る

念のため補足しますが、ギャグではありません。

体得したものに現れるという魔闘気。これを相手にぶつけることで空間を歪め、受けを取れなくするのです。魔闘気に翻弄され、オロオロしている間に一撃。と、そういうわけです。

対して北斗神拳には「これだ!」という奥義は存在しません。*2 これはどういうことでしょうか。

決まらない。固まらない。

原作、アニメ問わずご覧になった方は「いっぱい技あるなー」という感想を持った方もいらっしゃるかと思います。 北斗神拳はもちろん、人の技を使ったりもするわけです。

この理由は物語の後半、カイオウを追い詰めた際に当のケンシロウによって語られます。

北斗琉拳は魔闘気により敵を幻惑し、敵の受け技を流すことを極意とした。 しかし北斗神拳は戦場の拳。千変万化する戦いの中にこそその奥義を見出したのだ!!

つまりは

  • 一つのことにこだわり過ぎて結局過去の過ちを繰り返してしまった北斗琉拳

  • 場に合わせて適切な解を見出し続けた北斗神拳

と、こういう構図になるわけです。これ即ち

  • 「ベスト」で「唯一」な解法(プラクティス)など無い。

という銀の弾丸の教えそのままではないでしょうか。

「これさえやっていれば勝てる!」という手段は、派手であり、確かに直近は猛威をを振るうかもしれません。ただし「もしそれが破られたら?」への解答は、かなり厳しいものになってしまうでしょう。

おわりに

さてここまで北斗の拳を用いて銀の弾丸について考えてみました。

改めて考えてみると、劇中においてケンシロウは

  • 水もマトモに飲めてないボロボロな状態

から

  • どの様な強敵にも立ち向かい、乗り越える戦士

へと成長していきます。

その過程では、強敵との遭遇もさることながら「個々の経験(戦い)から学び(悲しみ)を得、次へ活かす」というケンシロウの生き方スタンスが描かれているわけです。

なんと、そのスタンスすら、我々エンジニアの目指すべき姿ではないでしょうか。 もちろんこのスタンスが北斗神拳伝承者たるものなのか、ケンシロウ独自のものなのかは定かではないですが、いずれにせよ素晴らしい姿勢であることには変わりありません。

北斗の拳にはまだまだ学べることが多いように感じてします。

*1:元斗とかはとりあえずおいておいて

*2:有名・無名はあるでしょうが

いろふさんと遊びたいん?ほれだったらvimでしたらええでよ! #irof_history

はいどうも。 いろふ advent calendarの18日目。 昨日は @ravencodingさんで、明日は@grimroseさんがやるいよるで。

そうそう

別になんちゃでないんやけど、うちんくの方言で書いたらどなんなるんだろと思てな。*1 ちょっとそれで書いてみるけん。
言うてもうちんくの方言いうたらまんで標準語とついやけん、ごつげにむつかしてなんちゃ分からんとか、やぎろしてじょんならんとか、そななんにはならん思うけんな。まぁ分からなんだら誰ぞにでも聞いていたよ。

いろふさんがおるところ

まーたいがいはいっぺんやにへんは「あーいろふさんと遊びとーていかんがー」いうんになったりしとるわの?ほんだけんどいろふさん言うたらだ、「いろふはどこにも居ないが、どこにでも居る」いうて言われよるぐらい、居るんや居らんのやどなんなっとんぞみたいな人じゃわな。

ほんだけどほれ、よーに考えてみーだ。いつも居るとこがあるでないん。 twitterや、twitter

ごーとぅーついったー

そうじゃそうじゃいうてブラウザやしょったらいかんでよ?ほれだ。そこにあるだろで?vimじゃ、vim。ついったーするいうんだったらほらvimでやっじょるわの?

ぷらぐいんっ!

なんちゃ言わんでええけん、とりあえずプラグイン入れたらええでよ。

https://github.com/datsuns/vim-metarw-irof

これな、twitvimmetarw いうんがいるけん。一緒に入れといていたよ。

ひょっとしてvimのプラグイン管理 のんとか使いよんやったら

Bundle 'datsuns/vim-metarw-irof'

とかでいけるけんな。

あそぶ!

いれれたな?ほんだらまずはいろふさんに会いにいかないかんの。

:e irof:

したらええけん。*2 *3

ほんだら画面が上下に分かれる思うんや。

上っかわはいろふさんの検索結果な。いつもでいろふさん見れたらええだろで思てな。

ん?下っかわ?そななん聞かいでも分かるだろでー。 いろふさんに決まっ・・・

  
_人人人人人人人_
> 突然の手抜き <
 ̄^Y^Y^Y^Y^Y^Y^ ̄

*4

まぁかまんかまん。細かいことはええんじゃわだ。いろふさん心が広いけんナニしてくれるわだ。(きっと

あとは

下っかわになんぞでも書いたらえんや。

ほんで保存な。

:w

ほんだらカーソルがおったとこのぶんをツイーっとするけん。

かしこいんはもう分かっとるわな?みなは言わんでよ?*5

いよっしゃーー!

いろふさんと遊べるでよー!!*6

続きは

:h irof

でなー。ほんだらなーー

*1:東讃な

*2:最後の:もいるでよ

*3:コマンドがeditなんはこらえていたよ

*4:丸が書けなんだが

*5:語尾に「ろふ」が付くでよ

*6:まぁいろふさんになっとる言うたほうがええんかいの

らむだる。

先日のBMG読書会の懇親会でもちょっと話題になったので、今日ちょっと仕事サボってc++のlambda(ラムダ)を調べてみたんです。

http://cpplover.blogspot.jp/2009/11/lambda.html

ここの内容をぽちぽち写経しまして。 まだ途中だけど、「お〜〜おもしろいなぁ〜〜!」という感じでした。

で、このラムダを使うシーンって何があるんだろうな?と思ったんですね。 OOPなコードに突如登場するの?とかね。

で、多少ぼやいてるとよさそうなサイトを紹介してもらえました。

http://d.hatena.ne.jp/faith_and_brave/20110520/1305873112

pdfを落としてワタシの死にかけSO-01Bで読んでたけど、なんだかもや〜っとした感じが拭えなかったので、ちゃんと腰据えて和訳をしてみようかなと。 ちなみにワタシのmacはgcc4.7を入れてるのでちゃんとビルドできるわけです! *1

以下pdf資料の意訳

Syntax

[ キャプチャ ] ( パラメータ ) -> ret { 処理; }

サンプル

int sum = 0;
long long product = 1;
for_each( values.begin(), values.end(), [&](int i){
  sum += 1;
  product *= i;
} );

[ キャプチャ ]

  • 外側の変数を値、または参照を指定してキャプチャする

( パラメータ )

  • 引数を指定。空でもOK

-> ret

  • 新しい記法。0ないしはひとつの戻り値の場合はオプション。

{ 処理 }

  • ラムダの処理本体を記述する

ラムダの定義よりも前にあるスコープ内の変数を指定。

  • 変数名指定でコピーキャプチャする。呼び出す際は指定なしでもOK
widget w;
auto lamb = [w] { for(int i = 0; i < 100; i++) f(w); };
lamb();  // 指定なしで呼び出し可能

 

  • 変数を参照指定でキャプチャする。
widget w;
auto da = [&w] (const int & i){ return f(w, i); };
int i = 42;
da( i );   // 有効なスコープ内ならラムダ本体より後で定義された変数でもOK

 
よってラムダの構文は以下の様な意味合いになる

// ラムダ構文 [capture](params){statements;} と同義なクラス
class __functor {
private:
  CaptureType __captures;
public:
  __functor( CaptureType capture ) : __captures( capture ){}
  auto operator() (params) -> ret { statements; }
};

 
実際の記述で考えると

// [c1, &c2] {f(c1, c2);} と同等なクラス
class __functor {
private:
  C1 __c1; C2& __c2
public:
  __functor( C1 c1, C2& c2 ) : __c1(c1), __c2(c2){}
  void operator()() { f(__c1, __c2); }
};

 

// [](P1 p1, const P2& p2) { f(p1, p2); }
// 原文では共通な箇所は省略されていると思われる
class __functor {
public:
  void operator()(P1 p2, const P2& p2) { f(p1, p2); }
};

// "auto"はc++11から
// [] (auto p1, const auto& p2){ f(p1, p2); }
class __functor {
public:
  template<typename T1, typename T2>
  void operator()(T1 p1, const T2& p2) { f(p1, p2); }
};

 
大体ポリモフィック構文 *2

// [](p1, p2){ f(p1, p2); }
class __functor {
public:
  template<typename T1, typename T2>
  void operator()(const T1& p1, const T2& p2) { f(p1, p2); }
};

ローカルネスト関数

GotW #53に、このような書き方が便利だと書かれている、が、正しくない

int f( int i ){
  int j = i*2;
  int g( int k ){  // 関数f内にネストした関数定義は書けない
    return j+k;
  }
  j += 4;
  return g( 3 );   // ローカル定義した関数を予防としている

 
このコードにどのような意味合いがあるかを考える

  • オリジナルコードは関数gは、変数 j=i*4 を呼び出し箇所で使用したい意図がある=> 参照によるキャプチャになる
  • 時々関数gの定義箇所で変数 j=i*2 を使用したい場合もあるだろう => 値によるキャプチャになる  

今日のC++による代替案

いくつかの代替案がある * 幾つかはシンプルだが使いにくい * それ以外は複雑だが早い  

代替案1:ローカルクラス(複雑で、もろい)

関数をローカルクラスでラッピングする。関数はオブジェクトを介して呼び出す

int f( int i ){
  int j = j * 2;
  class g_ {
    int &j_;                 // ここでは参照を選んだ
  public:
    g_( int &j ) : j_(j){}   // ここでも同じ参照を
    int operator()( int k ){
      return j_ + k;         // 大元の変数jに参照アクセスする
    }
  }g(j);                     // 変数のキャプチャを期待
  j += 4;
  return g(3);               // ローカル関数の呼び出し
}

c++03ではこのようなローカルクラスでのインスタンス生成はできない。c++0xでは記述は可能だがその欠点は消える。

代替案2:関数内に変数をローカライズする

(よりシンプルで拡張性がある。ただし参照キャプチャしか使えない)  
functorクラスのスコープ内にキャプチャされた変数自身を持っていく。関数も同じく。

int f( int i ){
  struct AllLocals_{
    int j;            // キャプチャする変数を宣言
    int g()( int k ){
      return j + k;   // 変数jに直にアクセス
    }
    void x(){ /*...*/ } // 拡張案
    void y(){ /*...*/ }
    void z(){ /*...*/ }
  }local;

  local.j = i * 2;
  local.j += 4;
  local.x();
  local.y();
  local.z();
  return local.g(3);  // ローカル関数の呼び出し
}

 

ラムダならこうなる

オリジナルコードにさかのぼって考える。理想的には

int f( int i ){
  int j = i * 2;
  auto g = [&]( int k ){  // ローカル関数の定義
    return j + k;
  };
  j += 4;
  return g( 3 );          // 関数呼び出し
}

値でキャプチャしたい?なら & を = に変えればOK。 
 

Effective STLより

  • item 43: 手元のループを楽にするアルゴリズム

  • STLにはループをしてくれるアルゴリズムが幾つかある

for_each transform copy find remove

なぜこれらのアルゴリズムが必要になったのか?大きな理由は以下のとおり

  1. パフォーマンス
    • 一部的に:コピーコードを防ぐ
    • 一般には:コードが最適化される
  2. 正確さ
    • エンバグする可能性を下げる。特に難解なイテレータ無効化によるバグなど。
  3. 明瞭さ
    • アルゴリズムの名称が、それが何をしようとするかを示しており、forの様に中身を読む必要性をなくしている。より意味付けされた用語を使用することで抽象化した記述が可能になる。

Effective STL 20項より

直接記述したループ

for( auto i = strings.begin(); i != strings.end(); ++i ){
  cout << *i << endl;
}

 
ラムダを使用しないSTL

copy( strings.begin(), strings.end(), ostream_iterator<string>(cout, "¥n") );

 
STLとラムダを併用

for_each( strings.begin(), strings.end(), [](string& s){
  cout << s << endl;
});

どれが読みやすいコードだろう?for?copy?for_each?  

多くのループはワンライナーにできない?

直接記述したループ

for( auto i = strings.begin(); i != strings.end() ++i ){
 // イロイロ処理
}

 
ラムダを使用しないSTL

for_each( strings.begin(), strings.end(), LoopBodyFunction(...) );
 // さぁループの本体を書くんだ!!

 
STLとラムダの併用

for_each( strings.begin(), strings.end(), [&](string& s){
  // いろんな処理
} );

 

視点を変えて:for_each VS for( x:coll )

STLとラムダを併用して呼び替えたコード

for_each( strings.begin(), strings.end(), [](string& s){
  cout << s << endl;
} );

 
c++0xではもっと素敵なforループがある

for( auto& s : strings ){
  cout << s << endl;
}

ただし、

  • なんだか前と変わってない?
  • 他のアルゴリズムではこのように記述できない

つまりこんな書き方はできないってこと。

for_each( strings, []( s ){  // 将来的にはこう書ける様になるかも
  cout << s << endl;
} );

 

Effective STL 43項から

vの要素から条件「>x と <y」に合致するものを検索する。 直接記述したループならこうなる

auto i = v.begin();        // iは後で使うのでループの外で宣言
for( ; i != v.end(); ++i ){
  if( *i > x && *i < y ) break;
} 

 
ラムダを使わないSTLなら(c++03)

auto i = find_if( v.begin(), v.end(), 
                  bind( logical_and<bool>(),
                   bind( greater<int>(), _1, x),
                   bind( less<int>(), _1, y ) ) );

真剣に書いてコレ。  
ラムダを使うと

auto i = find_if( v.begin(), v.end(), [=](int i){ return i > x && i < y; } );

非常に簡潔になる。

ラムダにより幾つかのSTLアルゴリズムを置き換えられる

vectorを使ったベタなループ

int sum = 0; long long product = 1;
for( auto i = value.begin(); i != values.end(); ++i){
  sum += +i; product *= +i;
}

 
ラムダを使わないSTL

int sum = accumulate( c.begin(), c.end(); 0 );
long long product = accumulate( c.begin(), c.end(), 1, multiplies<int>() );

Multi-passだと遅い。また変数のゴミが残る。  

ラムダとSTLの併用

int sum = 0; long long product = 1;
for_each( values.begin(), values.end(), [&]( int i ){
  sum += i; product *= i;
} );

accumulate()よりもより簡潔に、かつ可読性が上がる  

パフォーマンスメモ

以下の変数strが与えられたとする

char str[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

基本のポインタによるループ

char *end = &str[52];
for( char *p = str; p != end; ++p )
  ++*p;
}

for_each、イテレータとラムダを用いる

array_range<char> r(str);   // バウンダリチェック済イテレータラッパー
for_eacch( r, [](char& c){ ++c; } );

array_range()はプロトタイプでまだ採用されていないものである点に注意。 VS2010 RC on Windows7のdebugビルドにて計測すると、最悪値でポインタループの1.8倍の速度であった(最善値では0.8倍)

これらから学んだこと

  • もう「手書きループのほうが良い」なんて言えないよね?
  • ラムダを使うことにより、algorithmは更に良くなる
    • パフォーマンスの向上
    • 正確性、デバッグのしやすさ
    • 明確さ。可読性の向上。
  • 幾つかのSTLを置き換えることすら可能

  • ラムダは任意長の本体と同様に動作する *3

    • 明らかな結果:すべてのalgorithmが言語機能としてループとなりえる
    • その意味:アナタの書いた任意のalgorithmが含まれる
    • 結論:新しいループや機能がほしいでしょう?さぁ"関数"を書こう。言語に新しいループ機能を追加するように。。。  

新しいアルゴリズムを書いたよね?

全要素について"step"を実施する

template<typename C, typename F>
void for_each_nth( C& coll, step s, F func){
 ...
}

 
新種のループが書けるよ!

// for every 3rd element in v...
for_each_nth( v, 3, []( Widget& w )){
 ...
});

 

どんなループでも・・・

C++のループの代わりに

do{
  // do this while ...
)while( !Done() );

 
もっと簡単な書き方

do_while( [&]{
   // do this while ...
}, []{ return !Done(); } );

 
もしパスカルライクな記法がお好みならrepeat_untilを使ったループもある

repeat_until( [&]{
   // do this until ...
}, []{ return Done(); } );

 

どんな種類のalgorithmでも

こんなC#/Javaなコードの代わりに

lock( mutX ){
  ... use x ...
}
synchronized( mutX ){
   ... use x ...
}

こんな書き方ができる

{
  lock_guard<mutex> hold( mutX );
   ... use x ...
}

もしC#/javaライクなスタイルがお好みならそれも簡単にこう書ける

lock( mutX, [&]{
  ... use x ...
});

synchronized( mutX, [&]{
  ... use x ...
});

 

アクティブオブジェクト:対象にしているのは

  • 独自のスレッドで動作するアクティブオブジェクト
    • 各オブジェクトは非同期エージェントである
  • メソッド呼び出しは非同期メッセージングとなる
    • メソッドシンタックスは理解しやすく、コードも汎用的になる *4
    • アクティブオブジェクトスレッドのメインラインはメッセージパンプである。シーケンシャルなメッセージ処理はそれらをお互いにアトミックにし、内部・外部にロック構造を必要としない    例)
class Active {
public:
  void Func(){ ... }
};

// 呼び出しコード。アクティブオブジェクトを使用する
Active a;
a.Func();    // ノンブロッキングな呼び出し
 ... more work

class Backgrounder{
public:
  void Save( string filename ){
    a.Send( bind( &Backgrounder::DoSave, this, filename ) );
  }

  void Print( Data& data ){
    a.Send( bind( &Backgrounder::DoPrint, this ref(data) ) );
  }   // ref()が重要!

private:
  ...       // スレッド内部データ
  Active a; // スレッドメッセージキュー、ループをカプセル化
  void DoSave( string filename ){ ... }
  void DoPrint( Data& data ){ ... }
};

 

class Backgrounder{
public:
  void Save( string filename ){ a.Send( [=]{
    ...
  }); }

  void Print( Data& data ){ a.Send( [=, &data] {
    ...
  }); }
private:
  ...       // スレッドローカルデータ
  Active a;
};

何を学べた?

  • 堅牢なクラス定義を通常の記述と変わらない方法で記述できた
  • ヘルパーメンバを追加して、単に "a.Send([=] {"という風に各メンバ関数の先頭に記述すれば良い
  • 言語に組み込まれているアクティブオブジェクトがなくっても悪くはない

シーケンシャルループ

Foo()を配列の各要素に適用する

void DoFoo( float a[], int n ){
  for( int i = 0; i < n; ++i ){
    Foo( a[i] );
  }
}

同じことがstd::for_eachでできる

void DoFoo( float a[], int n ){
  for_each( &a[0], &a[0]+n, [](float f){
    Foo( f );
  } );
}

Intel TBB parallel_for

最初にファンクタを定義する

class ApplyFoo{
  float *const my_a;
public:
  void operator(){ const blocked_range<int>& r ) cconst {
    float *a = my_a;
    for( int i = r.begin(); i != r.end(); ++i ){
      Foo( a[i] );
    }
  }

  ApplyFoo( float a[] ):my_a(a){}
};

次に、parallel_forを使う

void DoFoo( float a[], int n ){
  parallel_for( blocked_range<int>(0, n), ApplyFoo(a) );
}

Intel TBB 2.2+でラムダったparallel_for

void DoFoo( float a[], int n ){
  parallel_for( 0, n, 1, [=](int i){ Foo( a[i] ); } );
}

こっちの書き方も可能

void DoFoo( float a[], int n ){
  parallel_for( 0, n, 1, [=](int i){
    Foo( a[i] );
  } );
}

 
(このあと同じコードの VS2010のサンプルがあるけど省略)

スケーラブル並列処理

このシーケンシャルなコードを考えている

void SendPackets( Buffers& buf ){
  for( auto b : bufs ){
    Decorate( b );
    Compress( b );
    Encrypt( b );
  }
}

 
ループ内で呼び出している関数について考察する * お互いに副作用は無いと仮定する(もしそうならパイプラインループが可能) * 処理されるパケットの順は不同とする(もしそうなら完全な並列化が可能)

分割して統治

スレッドプール、もしくはPPLタスクグループであるpoolが与えられたとする

void SendPackets( Buffers& bufs ){
  auto i = bufs.begin();
  while( i != bufs.end() ){
    auto next = i + min( chunkSize, distance(i, bufs.end()) );
    pool.run( [=]{
      for( i = first; i != next; ++i ){
        Decorate( *i );
        Compress( *i );
        Encrypt( *i );
      }
    } );
    i = next;
  }
  pool.join();
}

何を学んだか

  • ラムダは並列ループ、分解、fork/joinなどが簡単に記述できる
    • ループ内部が並列に実行されることに寄り、アルゴリズムの一箇所だけに集中できる
    • コード自体も構造化な状態が維持される。これ重要。

初期化

"tohava"によるサンプル

int x = 1;       // constであるべき、だが
for( int i = 2; i <= N; ++i ){
  x += i;        // この様な幾つかの処理が必要な初期化もある
}                // この場合にxは単純にconst宣言はできない
// この時点からxがconstになるべきである

この様なxをどうやってconstにしようか??

案1 : まぁまぁ

初期化処理を関数に分割する

int XInit( int N ){
  int ret = 1;
  for( int i = 2; i <= N; ++i ){
    ret += i;
  }
  return ret;
}

その後、、、

  const int x = XInit( N  );  // 成功する
  • 利点:動く
  • 欠点:局所性を失う。関数名の爆発(AInit, BInit, CInit....)

案2 : もっと良い方法 オリジナルコードをラムダ化する

const int x = [=]{
  int ret = 1;
  for( int i = 2; i <= N; ++i ){
    ret += i;
  }
  return ret;   // 戻り値でxを初期化
}();
  • 利点
    1. 動く
    2. 局所性を失わない
    3. 新しい関数名不要

初期化 : さらなる問題点

David Svobodaからの変数のサンプル

int x = 0;  /* ダミー値 */
try{
  for( int i = 2; i <= N; ++i ){
    x += Calc(i);      // この値で初期化!
  }
}
catch( XInitException ){
  x = 0;               // か、例外時は0で!
}
// ここ以降xはconstになるべき

 
ラムダを用いた解決案

const int x = [=] -> int{
  try{
    int ret = 0;
    for( int i = 2; i <= N; ++i){
      ret += Calc(i);
    }
    return ret;
  }
  catch( XInitException ){
    return 0;
  }
}();

<< p.32からは今度追記する >>

*1:win機のcygwinでトライしてみたけど何か上手くいってない・・・

*2:原文:Possible Alternative Polymorphic Syntax

*3:原文:work equally well for bodies of arbitrary length

*4:原文: lets generic code treat active and ordinary objects uniformly

DevLOVE関西 2012 (でLTしました) #DevKan

行って来ましたーー

http://devlove-kansai.doorkeeper.jp/events/1757

着くまで

平常運転でした。

A-1:「乙女ゲーを支える技術 - play2.0 + Scala の開発事例 -」

変態さんだー!

採用した技術要素の何もかもが初めてづくしという非常にアグレッシブというか、無茶しやがって・・・というか。 一応個人的には「c++rubyをがんばろう」と、ついこの前決めたのですが、相変わらずscalaよさそうだなーー、という感じが。

そういえば「scalaのwebフレームワークならplay一択」という事は分かったのですが、「じゃぁなんでscalaにしたの?」はわからなかったかも。(懇親会でも聞きそこねてしまった)

あと、「最初の一ヶ月はほぼ進捗がなかった」ということだったのですが、

  • その状態でも「頑張る」になれた理由

をもう少し聞きたかった。。。 新しい技術要素を導入した時って、絶対「やっぱ辞める」と「いやいや頑張る」のせめぎあいだと思うんですよね。 どこでも似たような感じにならないかなー、、とか。

B-2:メンバーの行動が激変!「ペアふりかえり」ワークショップ

  • 毎日のKPT
  • なぜなぜ分析

によって教育するという。 マンツーじゃないと教える側は大変かもですが、強力だろうなーと。 まぁ、ワタシなんぞに部下が付くのかはわからないですが、今やってるふりかえりでもなぜなぜ分析的なやり方はもう少し取り入れようかと思ったり。

A-3:なぜ私はソニックガーデンのプログラマに転身できたのか?

五年後の自分、大丈夫ですか?

・・・・がはっ

常々「地元に帰って仕事する」をどうやってやろうか悩んでるわけですが、気が付けばこんな年なんですよね。時間はあまりない。 wjapc(http://www.wjapc.jp/)とかでも地域へのフォーカスとか考えたいなと思ってますが、もっと具体的なアクションが必要だな、と。

B-4:やる?、やらない?の対立を考える。

TOCTOC! 教育への適用を考えるTOCfE (TOC for Education)

考え方としては非常に興味深い。。。 幾つかツール(ブランチ etc.)があるらしいので、その使い方だけでも抑えようかな。

クロージング

グループ別で誰かの課題の解決策を考える。 が、ワタシのグループの方の問題は厳しかった。

  • 質問"だけ"に集中して、課題を浮き彫りにする

というステップがあったけど、どんどん暗闇に落ち込んでいく感じがやばかった。。。 で結局「転職しましょう」とか言ってしまってたわけだけど・・・。

懇親会

先日のAgile Tourで脅迫されてお誘いいただいたのでやってみました。 slideshareに!と思ったけど使ってる画像がアレかもなのでやめておきます。。。*1

概要

  1. やってることがネストし過ぎてアレ
  2. ポモドーロがいいらしい
  3. けどめんどくさい
  4. ネスト具合と時間さえわかればええか
  5. じゃぁvimで(ry

相変わらず前ふりが長すぎて綺麗に時間切れ。まぁ練習した時にも明らかに収まらなかったので時間足りないのわかってたんですけどね。

一応これくらいの記録はできるようになったよー、という画像

f:id:datsuns:20121111163503p:plain

やってみて分かったけど意外とネストしてないのよね。 まぁこの辺は別エントリにでも書こう。

その他

こちらも平常運転・・・・しちゃだめなんだけどな・・・。

*1:次からは素材の探し方も考えないとイカンなと