2010年12月5日日曜日

PHPの本当にあった怖い話(1)

筆者はPHPを仕事で使うようになって5年経ちますが、それは自慢になりません。
何故なら、未だに裏切られます。

結論から言いましょう。


$aaa = range("0","9");
$bbb = range("a","z");
$ccc = array_merge( $aaa, $bbb );
$ddd = array_search( "a", $ccc );


この$dddには何が入るのか?10が入って欲しいですよね?
0です。

この理由がいきなり説明できる人は、PHPの仙人もしくは関係者でしょう。
師匠もしくは、インサイダーと呼ばせてください。

実は最初のrangeが曲者です。
これは"0"と"9"を指定したにも関わらず、intの配列を作ります。
結果、$cccの中身にはintとstringが混在していることになります。

まだ前フリです。

そしてarray_searchで何が起こるのか?

"a"を探せ、って言ってるのに、intval("a")==0 で0番目が一致しちゃってるのです。

ありえなくね?

結論として、前述の例では、rangeとarray_searchの挙動の共犯です。

PHPは、こういった「暗黙の型変換」が大量にあり、warningすら出ません。
http://www.php.net/manual/en/types.comparisons.php

sortについては、これを反映して、「比較「型」の強制」があります。
http://www.php.net/manual/en/function.sort.php

しかし、array_searchには無いわけです。

初心者のうちは助けられることが多いと思いますし、
それを意図しての型変換でしょう。
しかし、中級になったとたん裏切られるのです。

これを期待通りに可動させるには、rangeの結果にstrvalを通す必要があります。


$aaa = array_map( "strval", range("0","9") );
$bbb = range("a","z");
$ccc = array_merge( $aaa, $bbb );
$ddd = array_search( "a", $ccc );


ありえなくね?

2010年12月3日金曜日

ifを書いたら負け

筆者の持論というか、
最近の見解で「条件分岐を書いたら負け」というものがあります。

具体的にはif,for,while,switchです。

プログラムと条件分岐は、切っても切れません。
しかも要件定義者は、プログラムに都合の悪い仕様を考えてくれるので、
馬鹿正直に実装すると、条件分岐で大変な事になります。

何が大変って、デバッグがです。
極論すれば、条件分岐を一つ書くと、バグの可能性が一つ増えることになります。

20年前には、ソースプログラム上のif文の数等を数えて、バグ件数を見積もって、
それに満たなければ「枯れてない」と判断する。そんな昔話もあるらしいです。

面倒な仕様は、実は仕方有りません。
面倒な仕事を面倒なままやるなら、誰にでも出来ます。

困難な問題は分割せよ、と昔の偉い人も言ったらしい。

もう少し具体的な話を書きます。
例えば、非連続値が関わってるタイプの条件分岐は、工夫次第で条件分岐を省けます。

簡単な話では、「「入力値」を幾つかの範囲にグルーピングするもの」。集計ではよくありますね。

perlではフツーに書くと、こんな感じになると思われます。

my @threshold = ( 20,60,100,150,200,500);
sub islevel{
my($value)=@_;
my $level = 0;
foreach my $ii (@threshold){
last if( $ii > $value ) ;
$level = $ii;
}
return $level;
}


#一応デバッグしたから正しい筈

  • 各範囲の閾値のうち、
  • 入力値より大きいもの
  • 直前が、範囲値

直前の値、が曲者で、地味に面倒な処理です。

この手の実装をするときには、大きい方から回した方が、
条件文はスムーズだったかな。

結論からいいましょう。
perlらしく書くとこうです。

my @threshold = ( 20,60,100,150,200,500);
sub islevel{
my $value = shift;
my @levels = grep { $_ <= $value } @threshold; return $levels[$#levels]; }


後者の考え方は実にシンプルです。

  • 各範囲の閾値のうち、
  • 入力値以下のもの全てを、
  • のうち、もっとも大きいものが、範囲値

余り変わってない?そうですね。「直前の値」の解釈を変えただけです。

本稿で論じたいことの肝は、
処理は多めにやっちゃったほうがプログラムはシンプルになる。
というところです。

ループと条件分岐は、grep に任せています。
第2引数に配列、第1引数に条件式を指定すると、
それにマッチした要素を全て抜き出してくれるという便利関数です。
関数型プログラミングの発想らしい?

これがサンプルをperlで書いている理由だったりしますが、
似たような考え方は他の言語でも出来ます。

grepで回せるように、要件を意訳した。と言った方が正しいでしょう。
性能についての議論はややこしくなるから割愛します。

って言うか、grep に相当する機能が無かったら
自分で作るぐらいが本当でしょう。
毎回、前者のループをコピペしまくるんじゃ頭悪すぎます。

2010年11月14日日曜日

mysqlかpostgresqlかNoSQLか

postgresqlとmysqlとどっちを使えば良いかは、
いやっちゅーほど繰り返されてきた議論ですが、
両者とも明確なメリット&デメリットがあり、
極論するなら、消去法で選択するしかありません。

postgresqlの場合

  • insertが比較的早い
  • updateを繰り返すと、性能が落ちてくる。vacuumが必要。
  • vacuumするには、テーブルロックが必要。な場合がある。
  • レプリケーションが未実装。
  • connectは比較的遅い。


mysqlの場合

  • readだけなら早い→そんな案件今時あるの?
  • レプリケーションが比較的簡単&安定→readonlyスレーブ増やして負荷分散、はよくある発想。面倒だが。
  • innodb insert/updateが激遅い。
  • sqlに方言がやたら多い。
  • 複雑なsqlを投入すると、最適化に失敗する。と言うか最適化する気がないっぽい。
  • connectは比較的早い。
  • myisamは結構壊れる。(しかも普通に使っていて)
  • innodbも稀に壊れる。(しかも普通に使っていて)


エンタープライズ業界なら、これの何れか、もしくはOracleでイイヤ、
って話になるでしょうけど、
ソーシャルゲームであると、データ量、時間あたりの処理量が
指数的に増大します。


テーブル分割に対する実装コストも馬鹿になりません。
であれば、NoSQLを使ってしまえ、というのは手です。

NoSQLは数有りますが、近頃実績が増えてるっぽいのが、mongodbでしょう。
http://www.infoq.com/jp/news/2010/10/4square_mongodb_outage
これらのトラブルも、過渡期であるが故です。

それにこのトラブルは、会員数300万人とかのレベルなんで、
仮にmysql/postgresqlで運用していたとしても、
別のトラブルが有ったであろう人数と言えましょう。知らないけど。

シャードの不均一化が原因だって話なんで、
最初から、細かいテーブル(コレクション)に分割してしまえば済む話かも知れません。
メモリ使用的にもその方が有利らしいし。
http://www.mongodb.org/pages/viewpage.action?pageId=18448682

こんな事が出来るのも、スキーマレス、create tableイラズだからです。

ソーシャルゲームの実装は、トモダチ1000人居るだけで大変

最近は、ソーシャルゲームが流行り?で、
作ってみようかって案件は少なくないと思いますが、
「一般的なポータルサイト」と同じように作ると、
かなり大変なことになります。

「一般的なポータルサイト」には無かった概念として、
「フレンド」が有るでしょう。
こいつのお陰で、内部の処理量が指数的に増大します。
よほど上手く作らないと、何処かがネックになります。

「ページビュー」だけで、負荷は計れない。ということです。

誰かさんがゲームを開始すると同時に、
彼のフレンドの個々の情報を取得する、
のはよくある話だと思いますが。

500人の人が、500人づつのフレンドを持っていたすると、
いきなり250000の検索をしなければならなくなります。

同時に来たら、と思うと、血の気の引く件数ですね。

どう考えても、想定してない人数です。
馬鹿正直に実装したらmysqlとかphpとか、
どっちかのメモリが足りなくなるでしょうね。

と言うわけで、ソーシャルゲームの実装の鍵は、
如何にフレンド処理をサボるか、
これに尽きるでしょう。

と言うか、「収益の出る集客」を目指すなら、
ン10万人を最初から想定しなければならない訳で
必然、性能だけじゃなくて、テーブルのサイズの心配もしなければなりません。
テーブルパーティショニングとか、シャーディングとか、
自力で実装するには面倒臭すぎます。

バックエンドに、mysqlとかpostgresqlとかは、
ぼちぼち無理なんじゃないでしょうか。って言うか、筆者はもう勘弁してほしい。

であれば、初めから「それらの機能を内蔵したNoSQLに全部お願い」
それも手だなと。思う次第です。

2010年11月6日土曜日

携帯電話ポーチ(ケース)はなかなか良いモノがない

筆者は、携帯電話だけでも、主要3キャリア。
さらにそれに加えて、デジカメ。
最近さらに、Bモバイルwifiも導入しました。

都合、携帯電話サイズのモノが、5台。

筆者は、人間としてはかなりダラシナイ部類であり、
自分の手のひらより小さいモノは失くしやすいのです。
買ったばかりの通勤定期(当時は磁気カード)を、
尻ポケットに入れたつもりで、スカッて紛失。
というのを2ヶ月続けてヤったことがあります。
#SUICA以前の、プリペイドも何度か。
#総額、n万円は。

と言うわけで、とりあえずショルダーバッグを常に携帯して、
かならず持ち物はそこに押し込む、という運用を続けてました。
それは概ね上手く行ってました。

しかし、より大きいトートバッグに入れて通勤する場合、
これまた今度は、「優れたショルダーバッグがなかなか見つからない」
という議論に派生します。
それは別エントリにしましょう。長くなるので。

マメな人なら、バッグinバッグ等の整理手段を駆使しているでしょうが、
ダラしない筆者が真似をしてみても、ロクな事になりません。

インナーバッグを取り出そうとして、そのフラップに引っかかってた
ケータイがそのまま吹っ飛んで、変な隙間に入り込んでしばらく見つからないとか。
いや、実話です。

定期は最近では無記名SUICAで済ませているので、
残る問題は、小型モバイル機の取り回しです。

もちろん世の中、「携帯電話ポーチ」はメジャーなニーズであり、
100円ショップですら扱っていたりするのですが、
いずれも大体、フラップもしくはストラップ付き。


正直、こういうのって、開け閉めが面倒だと思うのだがどうか?

かと言って、トートバッグ系だと、逆さまにすると落ちます。
肩にトートバッグを背負ったまま、モノを拾おうと前屈、
バッグの中身がザラザラ出てくるとか。いや実話です。

内側がウレタンになっていて、ある程度密着していれば、
摩擦で落ちてこない。しかも手で取り出せる。
というのがあれば、それで解決だと思うのですが、なかなか無いですね。

一回自作してみようかと、100円ショップのケータイポーチの内側に、
サッシ隙間テープを貼ってみたのですが、あっと言う間に剥がれました。

と思って探したら、ウレタンとかでは無いのですが、
クロックスのo-dialsというホルダーが良さげ。
注文してみようか。

2010年11月2日火曜日

金を稼ぐだけが一流か

tumblr経由で、気になる記事を見つけました。

http://d.hatena.ne.jp/yaneurao/20101029

全体的に、収入ベースでの物言いが引っかかります。端的に言えば卑屈。
収入について、何かコンプレックスを持っているのだろうか?

35歳の現場の技術者が「一流の技術者になるには英語と数学を勉強しておきなさい」とか言っても私ならそんな意見にはこれっぽっちも耳を貸さない。35歳なら、もうとっくにセミリタイアしていておかしくない年齢だ。その技術者はもしかしたら本当に一流の技術者かも知れないが、ビジネスマンとしては三流だ。みんなもそういう人が居たら、「英語と数学を勉強してもあんたのようにしかなれないじゃないか!」と言ってやるといい。(そのあとどうなっても知らんけど)

この箇所で自身が述べているとおり「ビジネスマンとして一流」を「技術者として一流」を混同している。
そして、この反論を聞いた相手は、

だからオレのようにならないために忠告してるんだ。

という反論が帰ってくるに違いない。

あと、自分を天才だと思っている若者に向けて一言言っておく。君は本当に天才かも知れん。だけど、もし本当に天才なら30年後なんか見据える必要ねーじゃん。10年ほどで結果を出せるだろ。なにせ、天才なんだからさ。だから見据えるのは10年ぐらいでいいじゃん。君が社会に出てから10年も働いたら2億円ぐらい手元にあるんじゃね?もし、君が本当に天才ならな。

これも穿った見方。

世間は、「天才の扱い方を知らない」。出る杭は打たれる。
ただ社会に出ただけでは金を稼げない。
自明ですよね「金を稼ぐ天才」ではないから。
仮にそうだとすれば、それは詐欺もしくは、それに近い何かでしょう。

技術の「天才」は、世間で活躍しにくいカテゴリだと思われます。
芸能や芸術は、一目見りゃ判る。
しかし、技術系は、判らない人に見せても「魔法」にしか見えません。
#ここで論じてる「天才」ならそのくらい行かなきゃ嘘でしょう。

要するに、「社会に出る」というか「雇用される」線で、
天才が活躍するのは困難だろう。というのが筆者の解釈です。

グーグルとか、外資系なら判らんでもないけど、
それは正しく、英語と数学が必須でしょう。
「一流の技術者」が言っているとおりにね。

真逆のアプローチとしては、
現代はインターネットが普及してるので、

アピール自体は困難ではないどころか簡単です。
一番現実的なのが、iPhoneアプリでも作ってしまうことでしょう。
ものすごい奴をね。それは何かは筆者には判りませんが。

これも英語版は必須でしょう。市場が比べ物にならない。
最近は物理演算の無駄遣いが流行ってるからひょっとしたら数学も要るかもね。

と言っている筆者は、「天才に似てるらしい、天才でない何か」なので、
間違ってるかも知れません。まあ間違ってるでしょう。今貧乏なんで。

2010年10月26日火曜日

app engine 新機能色々

筆者はGoogle app engineを非仕事で常用していますが、
オフィシャルブログ追っかけはやってないので、
結構出遅れます。

一つは remote_shell_api
もう一つは datastore_admin

remote_shell_api

データストアの中身を、直接CSV形式でダウンロードできたりとか、
app.yamlに書いて損無しのremote_apiですが、

- url: /remote_api  script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.p
  login: admin

恐らくその全機能をコマンドラインで試行錯誤しながら叩けるという
刺激的なツールです。

+ python2.5 ../google_appengine/remote_api_shell.py アプリケーション名
No handlers could be found for logger "google.appengine.tools.appengine_rpc"
Email: ########Password: App Engine remote_api shellPython 2.5.4 (r254:67916, Jan 20 2010, 21:44:03)
[GCC 4.4.1]The db, users, urlfetch, and memcache modules are imported.
アプリケーション名>

メッセージがチラっと出てる限り、

  • memcache
  • データストア
  • グーグル認証

等々は使えるようです。
まあデータストアだけでも、ご飯3杯。

直ぐに思いつくのは、データストアに対する、ややこしい操作。
datastore viewで1個づつ書き換えるにはどうよ?という操作。
これをスクリプトで一括処理しちまおう。というのは誰でも考えると思います。
しかしスクリプト書いてデプロイ、を繰り返すのはかなり面倒です。

アプリケーション名> class sitedata(db.Model):...
 pass...
アプリケーション名> sitedata.all().count()
16
アプリケーション名> 

こんな感じで、(感覚的には)直接クラウドの「あっち側」をイジれます。
実際には、remote_apiが、20レコード単位っぽい感じで送受信してるので、
スピードは早くはありません。

筆者はこれで、念願の、データストア一括削除ができました。

しかし、流石のグーグル。もっと良い方法があったという。

Datastore Admin

builtins:
- datastore_admin: on

これを書いておくと、datastore adminのメニューから、
「一括削除機能」が使えます。
しかも内部で、Map/Reduceを使って削除するらしい。

データストアに、

  • _AE_MR_MapreduceState
  • _AE_MR_ShardState

が増えたり
MainのLogsに

  • /_ah/mapreduce/worker_callbak
  • /_ah/mapreduce/controller_callbak

がドンドコ増えたりとか
Map/Reduceの一端が垣間見える体験です。