2011年12月27日

HTMLに「見た目ではなく意図を書く」という幻想

 『Coders at Work』を立ち読みした。
 Brendan Eichが10日間でJavaScriptを設計実装させられたと知り、ますます1994年のNetscape社を爆破したくなった。Netscape社がうまくいっていないことは製品からも見て取れたが(マイナーバージョンが上がるたびに同じバグをエンバグ→修正→エンバグ→修正と繰り返した事件を覚えている)、ここまでひどかったとは想像できなかった。アメリカのドキュメンタリー屋には、Netscape社がどんなクソの山だったかを証言するドキュメンタリーを制作してほしい。
 私には想像もつかないレベルでプログラミングしているお歴々のありがたいお言葉を拝読しながら、私は疑問に思った。誰かのクソを食ってクソをひりだしてそれをまた別の誰かに食わせる、あの世界との接点がどこにもない、と。

 
 誰かのクソを食ってクソをひりだしてそれをまた別の誰かに食わせる――これが実態を誇大に汚らしく表現していると思うのなら、あなたは幸せか、あるいはなにもわかっていない。「spモードシステムはIP網の中で動いており、パケットのIPアドレスとユーザーをひも付けるのは自然の発想だった」なる発言を見て、「まるでそびえたつクソだ」と思わずにいるのは不可能だ。そしてプログラマの大半は、こういうクソを食ってクソをひりだす運命にある。
 『Coders at Work』に登場する、天上の星々のごとき人々は、この運命をまぬがれているらしく、こういう経験について多くを語っていないように見える(私が読まなかった部分にあるのかもしれないが)。この人々はみな口を揃えて、「計算機科学と実社会のプログラミングのあいだにギャップがある」と語るのだが、そのとき彼らの頭のなかにある「実社会のプログラミング」なるものは、誰かのクソを食ってクソをひりだしてそれをまた別の誰かに食わせる行為を含んでいるのだろうか。
 
 「計算機科学と実社会のプログラミングのあいだにギャップがある」と語る人々の目には、ある種のギャップが見えないらしい。たとえばHTMLの、「見た目ではなく意図を書く」という奴だ。h1要素は「大きな文字」ではなく見出しを示す、というあれだ。CSSクラスの名前はredよりもwarningがいい、というあれだ。
 誰かのクソを食ってクソをひりだしてそれをまた別の誰かに食わせる世界では、人は、Webページをデザインするときにはあくまで見た目をデザインするのであり、意図をデザインするのではない。人は見出しではなく「大きな文字」を望む。警告ではなく「赤」を望む。ほかの人が見ればそれは見出しや警告に見えるのだが、それはただの妄想、幻覚であり、デザインする本人はそんなものはちっとも望んでいない。見出しに見える部分を指して「これは見出しのつもりだね?」と訊けば「はい」と答えるだろうが、それは結果論にすぎない。デザインしている真っ最中の本人の頭の中にあるのは、あくまで見出しではなく「大きな文字」、警告ではなく「赤」だ。
 私自身にしても、この日記で同じようなことをやっている。HTMLで正しく空行を作るのは少々面倒(というか設計の想定外?)なので、長らく全角空白+br要素ですませてきた。するとIE8からこれは空行を作らなくなり、IE9でも直る様子はなかったので、私はこの日記をIE7互換モードに設定した。私も意図ではなく見た目を書いてきたわけだ。
 「見た目ではなく意図を書く」なるやりかたは、「計算機科学と実社会のプログラミングのあいだにギャップがある」と語る人々のあいだではなんの問題もなく実行できるだろう。「実社会のプログラミング」の世界でもうまくいくだろう。しかし、誰かのクソを食ってクソをひりだしてそれをまた別の誰かに食わせる世界では、これはうまくいかない。
 
 Garbage in, garbage outならぬクソin, クソout、食物連鎖ならぬクソ連鎖の世界は、計算機科学の光明からあまりにも遠く、できることはなにもない――のだろうか。
 HTMLで見た目ではなく意図を書くべき理由は、つきつめれば、マルチユースと再利用にある。マルチユースとは要するにGoogleでうまくインデックスしてもらうことであり、再利用とは要するに将来ブラウザのレンダリングが変わってデザインが多少崩れても意味的には同じものとして読めること(私の空行作成法はこのテストに失敗した)、と言ってもいい。
 『Coders at Work』で誰だかが、「コードの再利用はうまく機能していない」と論じている。現在一般的なプログラミング環境が参照透明性を無視しているのが悪い、とその論者は言うのだが、まったく違うレベルで、私はその議論にうなずいた。そう、環境が悪い。必要なのは、「マルチユースや再利用を考慮して書け」というスローガンではなく、「見た目だけを書いたら半自動的にマルチユースや再利用に対応している」という環境だ。
 つまり、人に意図を書かせるのではなく、計算機が見た目から意図を読み取るべきだ。
 
 どんなソースに対しても100%計算機だけで完結する、というのは不可能だが、人間の読み取り作業――これは見出し、これは警告、と意図を識別してゆく作業――を支援することはできる。また、ソースの変更に半自動的に追随することもできる。1段落増えただけで意図の読み取り作業をゼロからやり直すには及ばない。
 「計算機が見た目から意図を読み取る」というやりかたは現在まだ主流ではない。もしかするとユートピア的に見えるかもしれない。しかし私の目には、「人間が見た目ではなく意図を書く」という幻想のほうがよほど砂上の楼閣であるように見える。それは、クソ連鎖の世界に見て見ぬふりをすることで成り立つ幸福の国だ。そんな幸福の国に暮らす計算機科学の光明がクソ連鎖の世界を救済する日がもしくるとしても、それは56億7千万年後のことだろう。

Posted by hajime at 02:13 | Comments (0)

2011年12月26日

iOS / Android / Web対応のアプリ開発

 人と生まれたからには、「ちょっと面白いんじゃないのこれ」と思ったが最後、それをしないわけにはいかない。最近の私の場合、flowerflowerの開発がそれだった。
 まず、App Storeというシステムの面白さ。かなり少額(現在だと85円)が扱えるし、おかしな業界慣行や相場を押し付けられることもないし、毎日でも値段を変えられるし、利用者は日本国内だけで少なくとも数百万人はいる。こういう面白いものを使って、物流時代の古いやりかたを続けるのは、もったいないの一語に尽きる。
 Webブラウザという、貧弱ながら統一されたプラットフォーム。いろいろ問題はあるが、コンテンツベースの商品がUIに売上を左右されることはない。
 実のところ、Androidは問題だった。私の見るところ、Androidは失敗しつつある。「デバイスの出荷数はiOSよりも~」と大本営発表はがなりたてるが、Android Marketの売り上げやAndroid端末の満足度は惨憺たるものだ。Androidとはつまるところ、反Apple連合の寄り合い所帯による焦土作戦であり、すべての利害関係者に損をさせるという莫大な犠牲を払ってAppleの覇権を食い止めようとするlose-loseのビジネスではないか。焦土作戦に訴えてまで食い止めるほどの価値がこの覇権にあるというなら、どうして日本国内の覇権を握っていたはずの日本のガラケーは今あの有様なのか。とはいえGoogleにもAndroidにも恨みはないので、試してみた。
 
 以上は開発を始める前の見通しであり、以下は現在の中間報告である。
 結論を先に述べると、
・PhoneGapは参考程度と考えるべき
・Android対応がコストに見合う条件はかなり限られる
・iOSのプッシュ通知はひどい

 
 PhoneGapについて。
 物の性質上どうしようもないことだが、バグだらけだ。私が見つけただけでも、PhoneGap自体のバグとしか思えない挙動が2つあった(iOSでデバイスを回転させたとき、CSSのビューポートの幅が変更されない。Androidでdevicereadyイベントが発火したときにネットワークが接続されていないと、navigator.network.connection.typeがnull)。もちろんこれにOSのバグ等も加わる。ちなみに一番往生したのは、Android 4.0のみアプリ内のHTMLファイルを開くときにクエリ文字列がつけられないことだった。
 とはいえPhoneGapがまるで役に立たないかというと、そんなことはない。「これをするにはおそらくこうするしかない」という見当をつけるうえで大いに参考になる。たとえばOS組み込みのダイアログボックスを出すとき、window.alertと同じようにスレッドをブロックしたいところだが、PhoneGapはコールバックしか提供していない。おそらく、こうするしかないのだろう。……というわけで、無駄なあがきに時間を費やさずにすむ。実現可能なことであればそのやりかたを見つけるのは比較的簡単だが、不可能という結論を出すには時間がかかる。
 
 Android対応がコストに見合う条件はかなり限られる。
 Androidは失敗しつつある。マーケティング面だけでなく、技術的な構想の面でも、失敗しつつある。その象徴がMenu IconsAction Bar Icons(両者は意味的には同一のもの)のデザインガイドラインだ。flowerflowerを開発しなかったら、この体たらくを知ることもなかっただろう。
 事細かなデザインガイドラインがこうも目まぐるしく変わるのは、しっかりとした技術的な構想が存在せず、「あれがウケなかったから今度はこれ」という真似をやっているからだ。経営が行き詰まったときに模様替えをして目先を変える店のようなものだ。店の模様替えはそう何度もやれるものではないが、Googleなら何度でも好きなだけAndroidの模様替えができる。なにしろデバイスを作って売って損をするのはGoogleではないのだから。
 iOSデバイスがしっかりとした構想の下に作られていることは、物理的なデザインにもUIデザインにも表れている。Androidデバイスが「あれがウケなかったから今度はこれ」であることは、物理的なデザインにもUIデザインにも表れている。そのやりかたに一部の消費者がつきあうのは勝手だが、アプリを作る側としては到底つきあいきれない。
 
 iOSのプッシュ通知はひどい
 意味のある動作をするソフトウェアを開発するとき、人は必ず摩擦に出くわす。摩擦がいつどこで生じるかは出たとこ勝負だ。flowerflowerの場合、最大の摩擦は、私の交通事故とiOSのプッシュ通知だった。どちらも約2週間を空しく貪り食われた。
 プッシュ通知を使うアプリを作ろうとする方々にご忠告しておくが、開発専用のデバイスをぜひとも用意すべきだ。それも複数台あるといい。デバイスを「復元」する回数がとてつもないことになるからだ。「復元」直後の状態でのテストを徹底的にみっちりとやらないと、Appleの審査を通らない。

Posted by hajime at 10:08 | Comments (0)

2011年12月23日

新作のご案内

 何年か前、エロゲーのシステムの仕事をしていたときのことです。私はプロデューサーに尋ねました。
 私:「世の中には名作といわれるエロゲーがたくさんある。名作だからといって必ずしも中古はプレミア価格ではなく、安く売られていることも多い。同人で流行った作品をやれば二次創作も楽しめる。新作を買う人はみな、そういう過去の名作をあらかたやり尽くしたうえで、さらに新作を買っているのだろうか?」
 プロデューサー:「いや、そんなことはないだろう」
 私:「安くて確実に楽しめるはずの過去の名作をやらずに、我々のような三流ブランドが出す海のものとも山のものともつかない新作を、税込9240円で買う人がこの世に何千人もいるのは、いったいどういうわけだろう?」
 プロデューサー:「さっぱりわからんね」

 
 私の読者の皆様にも、やはりお尋ねしたいところです。『新ナポレオン奇譚』『兵士シュヴェイクの冒険』『レストレス・ドリーム』、我が師・栗本薫先生の最高傑作『魔都』などの神々しい名作の数々はすでにお読みでしょうか? どうしても百合をというのなら、『荊の城』などは? もしそうでないとしたら、いったいどういうわけで私の作品などを?
 言い訳なら思いつかないこともありませんが、私の言うべきことではないでしょう。二流の酒や食べ物はそれなりに用いられて役に立つが、二流の詩はなんの役にも立たない――ホラティウスの言葉はいまだに真実です。少なくとも、真実であるべきです。このことを認めない人を私は尊敬しません。
 
 とはいえ、神々しい名作など、奇跡のように稀なものです。一流といえる作品さえ、容易に見出せるものではありません。もしこの世が理屈どおりに動く人ばかりなら、新作を読む人はおらず、たとえ名作が新たに書かれてもそれを知る人はおらず、そもそも新作が書かれることもないでしょう。
 (いわゆる「古典」を無邪気に信奉なさっているかたに一言。18世紀以降の「古典」は信用できません。それらはあまりにも多くの場合、ごく狭い範囲でほんの一瞬のあいだブームになって名前が売れただけの作品にすぎず、その質は現代の流行りものと大差ありません)
 もちろん現実には、新作を読む人も多く、そのため一流の作品は絶えず見出されつづけるのですが、おおまかに捉えれば、現実は上に描いたとおりである、と思います。
 生まれてから一度も小説を読んだことのない人は稀でしょう。しかし、めったに小説を読まない人は、この世の圧倒的大多数です。小説ではなくゲームやテレビドラマでも事は同様であり、「圧倒的大多数」がただの「大多数」になる程度です。
 この世の圧倒的大多数の人々は、人生のどこかで、小説を読むのをやめました。苦情や批判がましいことは言わず、黙ってただ読むのをやめました。こうした人々は、ほとんどの新作や「古典」がなんの役にも立たないということを知ったのだと、私は思います。
 
 この世に新作が現れるのは、それを読む人々がいるからです――いったいどういうわけでか。
 その謎の根源については詮索しないとして、では、この有難い人々は、どんな待遇を受けているでしょうか。
 エロゲー業界では、事態は唖然とするほど悪いものです。値段はより高く、質はほぼ常に凡庸かそれ以下。謎の根源には、これほどの悪条件をも覆す奇跡の力が備わっているようです。こんな奇跡を見せられては、「さっぱりわからんね」と言うほかありません。
 待遇面でもっとも納得がゆくのは、テレビドラマでしょう。新作は決まった時間に無料で提供されており、古い名作を見るには手間暇と費用がかかります。
 小説やゲームに比べて、テレビドラマを見るのをやめる人が少ないのは、テレビが無料だから、手軽だから、普及しているから、それだけでしょうか。その効果も当然あるでしょうが、しかしテレビには弱点も多いのです。たとえば、過去の作品はしばしばDVD化もされておらず、多少の手間暇では見ることができません。二流以下の作品――ほぼすべての新作は必然的に二流以下です――ばかり視聴者に見せておいて、「もっとテレビドラマを見てください」というのは理不尽な話であるばかりか、名作傑作をもたらそうとする作り手の気概をも損なうものでしょう。
 どの業界も、言い訳ならいくらでもできます。法律が悪い、業界の慣行が悪い、云々。それは各業界の当事者に任せておくとして、私は自分にできることを見つけ、実行しました。
 
 新製品の値段は、発売されたときが一番高く、時とともに下がってゆく――それがこの世の原則です。例外はもちろんありますが、まさに例外というべき珍しいものです。少なくとも、今のところは。
 この原則に支配されているかぎり、新作を読む人々に金銭面で報いることはできません。「金銭面は無理でも他の面で」という言い訳がどんなに空しいものか、私ごときが言うまでもないでしょう。
 「二流の詩はなんの役にも立たない」という言葉があてはまる分野では、値段は発売されたときが一番安く、時とともに上がってゆくべきです。
 
 一流や名作に高い値段がつくことには、おそらく異議はないでしょう。
 古い二流以下の作品は、たとえタダでも、ごくわずかな人々が読むだけです。そのごくわずかな人々の大半は、強い動機や事情(きっと大学の卒論に必要なのでしょう)のために読むのであって、バーゲンセールに惑わされて読むのではありません。バーゲンセールに惑わされる少数の人々をつかむために、大枚をはたく用意のある多数の上客から雀の涙ほどしか取らないというのは、間尺に合わない話です。
 今のところ現実がこの要請に反しているのは、作品が物理的な媒体に縛られているか、または、物理的な媒体に縛られていたときの慣習にとらわれているからです。
 すなわち、この要請に応えるには、
1. 作品を物理的な媒体から切り離す
2. 古い慣習との折り合いをつける
 という2つの条件を満たす必要があります。
 
 作品を物理的な媒体から切り離すにあたっては、媒体をネットに移すのは当然として、決済手段が大きな壁になります。少額を扱える、頻繁に値付けを変えられる、広く普及した、信用のある、手軽な決済手段が必要です。これらすべての条件を満たしたネット上の決済手段は、長らく存在しませんでした。
 「発売されたときが一番高く、時とともに下がってゆく」という原則に反するものは、貴金属のように希少か、あるいは企業(=株)のように変化し成長するものがほとんどです。ネットを媒体とする作品は無限に複製できるので希少ではありえず、また作品は発表されたときから変化しません。こういうものの値段が上がるのは、慣習に反することです。
 
 決済手段については、iOSのApp Storeが登場しました。これは上に挙げた条件をおおよそ満たすもので、たとえ疑問符がつくとしても「広く普及した」という点のみでしょう。また一応、Android Marketも数に入れておきましょう(私はほとんど期待していませんが)。
 作品は発表されたときから変化しない――なら連載にして、連載が進むにつれて値段を上げればいい、というのが私の回答です。この回答がどのように迎えられるかは知りません。それを知るために私はこうしています。
 かくして、連載配信プラットフォームflowerflowerと、それを使った作品『紅茶ボタン』ができあがりました。

Posted by hajime at 09:31 | Comments (0)

2011年12月20日

iOSのプッシュ通知まわりの驚くべきクソさ加減

 iOSのアプリを作っている。
 iOSは全体的に好感触で、Objective-Cさえ気にしなければ、あとはたいていなんとかなる。例外はプッシュ通知まわりで、これを作ったのは日本のITゼネコンかさもなければジョブズの愛人にちがいない。
 どうひどいか。
 
「一日待つか、端末の日付を進めて再起動しろ」。しかし実際はさらに悪く、端末をiTunesで「復元」するしかない
 プッシュ通知を使うアプリの初回起動時(正確には、最初にデバイストークンを要求したとき)のみ出るダイアログ(プッシュ通知をアプリに許可するかどうかをユーザに尋ねる)を出すには、端末をiTunesで「復元」するか、アプリを削除して一日待つか、待てなければ端末の日付を進めて再起動(電源をオフ→オン)するしかない。
 ……とAppleのドキュメントには書いてあるのだが、実際には後者のとおりにしても、問題のダイアログは出てこない(iOS 5.0.1)。一日待つのは問題外なので、つまるところ「復元」しかない。
 
・設定で3種類の通知(バッジ・アラーム・メッセージ)を全部オフにすると、アプリに渡すカスタムペイロードだけの通知が黙って握りつぶされる
 画面上では、バッジとアラームとメッセージをそれぞれオフにする、という形になっているのに、バッジともアラームともメッセージとも関係のない通知までが黙って握りつぶされる。
 
・プッシュ通知を一度も許可したことがない場合と、あとで設定でいったん許可してから全部オフにした場合とでは、挙動が異なる
 つまり、iOSは各アプリについて外から見えないフラグを持っていて、それはプッシュ通知を一度も許可したことがない場合にだけ立っている。
 どう違うかというと、そのフラグが立っているときはregisterForRemoteNotificationTypesを呼んでもコールバック(didRegisterForRemoteNotificationsWithDeviceToken)が起こらない。
 
 「頭のいいやつほどうたぐりぶかい…」とは『ワイルド7』の名言だが、iOSのプッシュ通知を使うときには猛烈に頭がよくなる必要がある。私はそこまで頭がよくないのですでに2回リジェクトされた。

Posted by hajime at 10:36 | Comments (0)

2011年12月13日

このコールバック地獄からの卒業

 JavaScriptの名前システムに日々キレている読者諸氏よこんにちは。もし私がタイムマシンで1994年に戻って「オサマ・ビン・ラディンを暗殺するか、それともネットスケープ社を爆破するか、どちらかを選べ」と迫られたら間違いなく後者を選ぶ。迫られなくても選ぶ。
 Tiny BASIC以来最悪と名高い(高めるのは私)JavaScriptの名前システムで誰もがまず困るのは、コールバック・スパゲィだ。それがどんなものか知らない幸運なかたはこちらを参照。よい名前システムは七難隠すで、多少のコールバック・スパゲィごときはどうにでもなる。よほどの場合でなければ同期オブジェクトを持ち出してもいい。が、JavaScriptにはどちらもない。
 そこで、世の中ではすでに山のようにコールバック・スパゲィ対策が打ち出されているが、デファクトスタンダードはない。
 私もひとつ対策を打ち出した。最初は、あまりの黒魔術ぶりに我ながら恐れをなして公表を控えようと思っていたが、世の中にすでにこんなにたくさん対策があるなら私がひとつくらい増やしたところでどうということもあるまい、と思い直し、ここに公表する。
 例1:


var sq = new Sequence([
	function () {
		alert('run 1');
		this.a = 1;
		setTimeout(this.$next, 1);
		return true;
	},
	function () {
		alert('run 2 this.a:' + this.a);
		this.$next('OK');
		return true;
	},
	function (arg) {
		alert('run 3 arg:' + arg);
	},
	function () {
		alert('never');
	}
]);

sq.Start();

 例1でのルールは簡単、
・関数オブジェクトを実行したい順番にArrayに並べる
・関数オブジェクト内からは、自分の次の関数オブジェクトがthis.$nextで見える
・this.$nextはsetTimeoutに渡されても$next内でthisを復元する
・次の関数オブジェクトを実行する処理を行ったときはtrueを返す
 とりあえず誰でもこれくらいはやるだろう。黒魔術はここから始まる。
 例2:


var sq = new Sequence([
	function () {
		alert('run 1');
		setTimeout(this.$1, 1);
		return true;
	},
	[
		function () {
			alert('run 2');
		}
	],
	function () {
		alert('run 3');
	}
]);

sq.Start();

 というわけで、
・関数オブジェクトを並べるArrayは入れ子にできる
・入れ子内の関数オブジェクトは、Arrayの前の関数オブジェクトからthis.$1、$2、...$nで見える
・入れ子内の関数オブジェクトでtrueを返さないと、外の次の関数オブジェクトに戻る
 ちなみに、
・入れ子内の関数オブジェクトからは外の次の関数オブジェクトがthis.$parentで見える。
 入れ子ができるので、こういうこともできる。例3:


var inner =	[
 	function () {
 		alert('run 2 this.a:' + this.a);
 		setTimeout(this.$next, 1);
 		return true;
 	},
 	function () {
 		alert('run 3');
 		this.$parent("OK");
 		return true;
 	}
 ];

 var sq = new Sequence([
 	function () {
 		alert('run 1');
 		this.a = 1;
 		setTimeout(this.$1, 1);
 		return true;
 	},
 	inner,
 	function (arg) {
 		alert('run 4 arg:' + arg);
 		var that = this;
 		setTimeout(function() {
 			that.$1();
 		}, 1);
 		return true;
 	},
 	inner,
 	function (arg) {
 		alert('run 5 arg:' + arg);
 	}
 ]);

 だいぶ黒くなってきた。
 タイムアウトとエラー処理と終了処理もあるんだぜ。


var sq = new Sequence([
	function () {
		alert('run 1');
		setTimeout(this.$next, 1);
		return true;
	},
	function () {
		alert('run 2');
	}
], function() {
	alert('onexit');
}, function() {
	alert('onerror');
}, 1000, function() {
	alert('ontimeout');
});

 ちなみにエラー処理はthis.$onErrorで見える。
 さて警告は十分したと思うので、実際に動くコードを以下に載せて終わる。


function Sequence(funcArray, onExit, onError, timeout, onTimeout) {
	this.fa = funcArray;
	this.onExit = onExit;
	this.onError = onError;
	this.timeout = timeout;
	this.onTimeout = onTimeout;
	this.contextSet = {};
	this.contextSetIndexCounter = 0;
}
Sequence.prototype._caller = function(f, context, args, loc) {
	if (context.$_isTimeouted || context.$_isExited) {
		return;
	}
	var lf = f;
	var lloc = loc;
	while (true) {
		this._prepareCall(lf, context, this.fa, null, lloc, 0, []);
		context.$self = this._generateFunc(lf, context, lloc);
		context.$_lastPassedTime = new Date().getTime();
		if (lf.apply(context, args)) {
			return;
		} else if (context.$_parent) {
			lf = context.$_parent;
			lloc = context.$_parentLoc;
		} else {
			break;
		}
	}
	if (context.$onExit) {
		context.$onExit();
	}
};
Sequence.prototype._generateFunc = function(f, context,  loc) {
	var that = this;
	return function() {
			that._caller(f, context, arguments, loc);
	};
};
Sequence.prototype._prepareCall = function(f, context, cfa, pf, loc, depth, ploc) {
	for (var i = loc[depth]; i < cfa.length; i ++) {
		var a = cfa[i];
		if (a === f) {
			delete context.$next;
			var c = 1;
			while (context['$' + c]) {
				delete context['$' + c];
				c++;
			}
			if (pf) {
				context.$_parent = pf;
				context.$_parentLoc = ploc;
				context.$parent =  this._generateFunc(pf, context, ploc);
			} else {
				delete context.$_parent;
				delete context.$parent;
			}
			for (var ii = i + 1; ii < cfa.length; ii ++) {
				var nf = cfa[ii];
				if (typeof(nf) == 'function') {
					var nloc = loc.concat();
					nloc[depth] = ii;
					context.$next =  this._generateFunc(nf, context, nloc);
					break;
				}
			}
			if (i  + 1 < cfa.length) {
				var n = cfa[i + 1];
				if (typeof(n) != 'function') {
					var c = 1;
					for (var ii = 0; ii < n.length; ii ++) {
						if (typeof(n[ii]) != 'function') {
							continue;
						}
						var cf = n[ii];
						var cfloc = loc.concat([ii]);
						cfloc[depth] = i + 1;
						context['$' + c] = this._generateFunc(cf, context, cfloc);
						c ++;
					}
				}
			}
			return true;
		}
		if (typeof(a) != 'function') {
			var lpf = pf;
			var lploc = ploc;
			if (i  + 1 < cfa.length) {
				lpf = cfa[i + 1];
				lploc = loc.slice(0, depth).concat([i + 1]);
			}
			if (this._prepareCall(f, context, a, lpf, loc, depth + 1, lploc)) {
				return true;
			}
		}
	}
};
Sequence.prototype.Start = function(context) {
	var that = this;
	if (!context) {
		context = {};
	}
	context.$_isExited = false;
	context.$_lastPassedTime = null;
	context.$_isTimeouted = false;
	context.$IsTimeouted = function(now) {
		if (context.$_isTimeouted || context.$_isExited) {
			return true;
		}
		if (that.timeout) {
			if (that.timeout + context.$_lastPassedTime < now) {
				context.$onTimeout();
				if (context.$onExit) {
					context.$onExit();
				}
				context.$_isTimeouted = true;
				return true;
			}
		}
		return false;
	};
	context.$onExit = function() {
		delete context.$onExit;
		if (that.onExit) {
			that.onExit.apply(context, arguments);
		}
		context.$_isExited = true;
		delete that.contextSet[context.$_index];
	};
	context.$onError = function() {
		if (that.onError) {
			that.onError.apply(context, arguments);
		}
		context.$onExit();
	};
	context.$onTimeout = function() {
		if (that.onTimeout) {
			that.onTimeout.apply(context, arguments);
		}
		context.$onExit();
	};
	var f = this.fa[0];
	context.$_index = this.contextSetIndexCounter;
	this.contextSetIndexCounter ++;
	this.contextSet[context.$_index] = context;
	this._caller(f, context, [], [0]);
};

var inner =	[
	function () {
		alert('run 2');
		alert(this.a);
		setTimeout(this.$next, 1);
		return true;
	},
	function () {
		alert('run 3');
		var that = this;
		setTimeout(function() {
			that.$parent("OK");
		}, 1);
		return true;
	}
];

var sq = new Sequence([
	function () {
		alert('run 1');
		this.a = 1;
		setTimeout(this.$1, 1);
		return true;
	},
	inner,
	function (arg) {
		alert('run 4 arg:' + arg);
		var that = this;
		setTimeout(function() {
			that.$1();
		}, 1);
		return true;
	},
	inner,
	function (arg) {
		alert('run 5 arg:' + arg);
		this.$onError();
		return true;
	}
], function() {
	alert('onexit');
}, function() {
	alert('onerror');
}, 10000, function() {
	alert('ontimeout');
});

function Tick() {
	var now = new Date().getTime();
	for (var i in sq.contextSet) {
		var ci = sq.contextSet[i];
		if (ci.$IsTimeouted(now)) {
			alert("timeouted");
		}
	}
}
setInterval(Tick, 500);

sq.Start();

Posted by hajime at 14:24 | Comments (0)

2011年12月05日

いいところもある

 最近、DoubleClick Ad Plannerというものを知った。ビジターの多いサイトのドメイン名を入れると、年齢・性別・学歴・世帯収入などが表示される。その信憑性には一定の留保をつけなければならず(後述)、絶対値としてはあまり参考にならないが、サイト同士を比較するには役に立つ。

 まず、「信憑性には一定の留保」というのは、たとえば下の画像をご覧いただきたい。
yahoo.co.jp
 これはyahoo.co.jpの結果である。
 世帯収入10,000,000~11,990,000円のところに不自然な突出がある。ネットサービスのアンケートに「年収1000万円」と回答する輩がこれくらいいる、というわけだ。こういう露骨な嘘ばかりではなく、何割か多めに回答する人も多いだろうから、絶対値としては鵜呑みにできない。
 「年収1000万円」の突出はだいたいどのサイトでも、yahoo.co.jpと同じくらいの大きさを示す。年収で見栄を張るのは男ばかりかと思ったら、そうでもない。
 stackoverflow.com:
stackoverflow.com
 shogi.or.jp:
shogi.or.jp
 cosme.net:
cosme.net
 見事に安定した数字だ。
 次に、下の図がなんのサイトかおわかりだろうか。
dmm.co.jp
 アダルトビデオ専門サイトの最大手(推定)、dmm.co.jpのものである。yahoo.co.jpと比較すると、300~500万円の層が大量に0~300万円に移っているほかは大差ない。この移動現象をどう見るべきかは難しいが、若年層の多さは影響を与えているだろう。たとえばshonenjump.com:
shonenjump.com
 でもやはり移動現象が起きている。これも信憑性を留保すべき点だろう。
 さてようやく本題に入る。
 上の2つと下の図を見比べていただきたい。
a.sofmap.com
 どの角度からどう眺めても、こう判断するほかないだろう――震え上がるほどの貧困の塊、と。
 この図は、エロゲー通販専門サイトの最大手(推定)、a.sofmap.comのものである。
 これほどの貧困の塊を相手に、よくもまあ商売をしているものだと驚きあきれる。市場規模が7年前(最盛期近辺)と比べて半分以下に縮小するのも当然どころか減少幅が少なすぎて疑わしい。この貧困ぶりについては稿を改めて論じるかもしれないが、さまざまな視点から広く話題にしていただきたいところだ。
 だがこの戦慄すべき図にも、ひとつだけ、いいところがある。
 「年収1000万円」の突出をご覧いただきたい。統計的に有意なデータが集まっているサイトで、この突出がこんなにも小さいサイトは、ほかに見つけることができなかった。

Posted by hajime at 14:09 | Comments (0)