l'essentiel est invisible pour les yeux

Sunday, December 16, 2007

ダムの水は決して漏らさず、溜め続け、勢いよく放流するのがCEOの役割である

この件でこのブログにもトラヒックが流れているので触れるべきだろう。

情報統制に関して思い出すのが次のエピソードである。(*1)

ジョブスは社員を集め、アップル最大級のキャンペーンThink differentのスタートを集う野外パーティーを開いた。席上、アップルの広告に対するThink differentを力説した。

さて、この日初めてジョブスと間近で接した一人の女性エンジニアは深く感銘し、以下に素晴らしいスピーチだったかを興奮しながら友人にメールした。

彼女のメール相手は、ソフトウェア企業家のデイブ•ワーナーという人物で、業界でもっとも影響力のある数百人の読者にメールマガジンを配信していた。そこには、マイケル•デルやビル•ゲイツといった面々も含まれていた。


翌日彼女の元には、ジョブスから「ハイ、スティーブです。話したい事があるんだけど」と留守電が入っていたようだが、幸い、彼女は、QuickTimeプロジェクトの重要人物であり解雇は免れたそうだ。

再建屋として崩れかかったアップルの組織を内部から立て直したのは、ギル•アメリオの功績が大きい。アメリオは、アップル再建に際してコスト管理と合わせて、情報統制を徹底して厳しくした。アップルの内部がブラックボックスである事は今も変わっていない。

今ドリコムは、社員が一丸となって、ダムに水を貯めているのに、少しずつ漏れている。ダムから水を放流した時よりも、漏れている事の方が話題になる。貯めても貯めても溜まらない水に、社員のモチベーションは枯れてしまう。

自分達が必死で貯めた水をCEOが勢い良く放流してくれる事が、社員の喜びであり働きがいである。
開発した製品のデザインが素敵と書かれれば、デザイナーは喜び、多くのブックマークがつけば、開発者は喜ぶだろう。水を漏らさず、貯め続け、そして、勢いよく流す事こそ、CEOの腕の見せ所であり、企画屋でありパフォーマーである内藤氏の力が最も発揮される領域だと私は思う。再建屋としての役割は、別の人物にエンパワーメントされるべきだろう。

今年の春に書いた事と重複する事が多いが、今のステージにおいても次の五つが重要である。

1. ダムの水漏れを防ぐこと。(情報統制)

2. 外部に対する水の放流先を一つに統一すること。つまり、今回の一件に関して早急に、CEOのブログからきちんと説明を入れること。楽観的に捉えれば、この一件はドリコムへの関心の高さに他ならない。ネット上を騒がせた事に関してCEOとして誠実な対応をとれば、新たな信頼が生まれる。社員•顧客•株主、全てのステークホルダーを不安な状態のまま放置してはいけない。また、CEO自らが引き続きドリコムの方向性を外部に対して(悪いニュース含めて)明確に示し続ける事。「悪いニュースはなるべく早く開示する事」はウォーレンバッフェトが投資先経営者に真っ先に求める要素の一つである。(情報公開)

3. もう一度社員のフロー状態を創りだすこと。ROEを上げる事で、短期的な利益を産み出すような、組織の最適化と名付けた過剰な人材流出はなるべく避けること。既存の社員のモチベーション低下にもつながりかねない。人無くして組織は成り立たない。長期的な価値創造を念頭に置き、社員のモチベーションを高めるビジョンを示す事。プロモーションすべき方向は、学生に向けてのファン獲得ではなく、社内の優秀な人材からの信頼を確固たるものとすること。それこそが、後の社外のファン獲得へとつながる。 (内発的動機付けとフロー)

4. 苦境を共に乗り越えようと一丸となってくれる人材の流出を避ける事と、今後は自分達より優秀な人間を雇う努力を惜しまない事。新卒の教育は時間とコストを伴う。今の時期は、まだ時期早々。(人材戦略)

5. 権力が内藤氏に集中しすぎてはいないだろうか?今の状況では、誰もCEOの経営上の決定を覆す事ができなく、CEOの判断ミスにより会社が共倒れになる可能性が非常に高い。分権化を進めるべきである。



この一件で悔しく思っている社員は多いはずである。
決して、この力を無駄にしてはいけない。ベクトルを合わせて、大きな力とするのがCEOのミッションであり、組織の成長と一人一人の自己実現に向けて努力する力が一致するべきである。ドリコムに関して、外野席からの発言は誰でもできるが、実際に行動に起こせるのはCEOである内藤氏と社員だけである。

*1 スティーブジョブス 神の交渉術 より引用&編集

CHANGELOG
5. を追加した。

P.S.
「何が言いたいのかわからない。身内からの批判は絶対に許すなってこと?」というご意見をid:hamastaさんよりぶくまコメントで頂いた。
必ずしも「情報統制 = (建設的な)批判封じ」とは考えていません。私は外部の人間なので、内情は詳しくわかりませんが、中の人のエントリを見る限りは、経営陣と従業員の距離感は近く、活発な議論が行われているようです。技術や市場の変化が著しいこの業界では、経営者と従業員の関係は、雇用主と雇用者というよりは、パートナーであるべきです。

しかし、トップからの情報公開無しに、各々の社員が独自の視点で会社や抱える問題を外に向けて語りだすと、多くのステークホルダーに不安を与える事になります。また、あらゆる交渉においてもこちらの内情が筒抜けでは、強い立場に立てません。適切な情報統制は、トップマネジメントからの適切な情報開示があってこそ成り立つ物だと考えています。伝言ゲームでは、元々伝えるべきだった内容に個人個人のバイアスがかかってしまいます。

配慮が足らず、言葉足らずな部分があったかもしれません。
ご指摘に感謝いたします。



元社員の戯言 or 建設的な批判

現在、ドリコムCEOブログからは、まだ何もコメントが発表されていない。
想定される対応は以下のあたりだろうか。

  • 「元社員の戯言であり、彼は最近大した仕事をしていなかった」と切り捨てる。
  • 元社員を気遣った上で、会社として反論する。
  • 元社員を気遣った上で、同氏の意見を尊重するコメントを発表する。
  • 時間が経てば騒ぎは治まるだろうと無視する。
どういう対応が取られるだろうか?
4つ目の対応は、メタ欲求の強い社員が各々にドリコムを語る事になり、会社の一貫性が損なわれかねないので避けるべきである。

分水嶺の法則(*1)
「苦難に耐えられないような弱い人間はストレスによって完全に打ちのめされてしまうが、耐える力を持った人間は、ストレスを切り抜けることで一層強化され、鍛えられるという現象」

負に傾いたバイアスをそっくりそのまま正の方向に変えてしまう力がトップの力である。


*1 心理学者 A.マズロー博士によって提唱された。要は、瀕死の状態から復活する事で、より強くなる戦闘民族サイヤ人のようなものと考えれば(ドラゴンボール世代にとっては)わかりやすい。

Friday, December 14, 2007

[WTF!] IE7のdocument.getElementByIDの挙動


<input type="text" value="Shun" name="first_name"/>
<input type="text" value="Mori" name="last_name"/>
<input id="first_name" value="rakuto"/>

<script type="text/javascript"> //<![CDATA[
document.getElementById('first_name').value; // => 'Shun' on IE7, 'rakuto' on Firefox 2.x and Safari 3.x
document.getElementById('last_name').value; // => 'Mori' on IE7, undefined on Firefox 2.x and Safari 3.x
// ]]></script>



The 'document.getElementById' means 'document.getElementByIdOrName' on IE 7.

Saturday, December 08, 2007

[Javascript] iPhone / iPod touchの「slide to unlock」のエフェクト(Effect.Illuminate)を実装

iPhone / iPod touchのTOPに表示されている'slide to unlock'のエフェクトです。

Effect.illuminate




Usage

Effect.Illuminate('illuminated_msg');


Option

Effect.Illuminate('illminated_msg', {color: '#fff', repeat: true});

color: スポットライトの色です。
repeat: エフェクトを繰り返すかどうか。
size: スポットライトのサイズ(文字数)

Source code
テキストノード以外は、子ノードに対して再起的にエフェクトを適用し、テキストノードの場合は、スポットライトを照らす関数を遅延呼び出しで実行する。同時にオプションで指定した文字数以上照らした場合は、スポットライトを消す(移動)するエフェクトを開始する。エフェクトを実行し終えたら、innerHTMLの内容を元の状態に復帰する。


var Effect = Effect || {};
Effect.Illuminate = function(element, options) {
var effect = arguments.callee;
element = $(element);
if(element.tagName) {
// Save the original style
with(effect) {
color = element.getStyle('color');
innerHTML = element.innerHTML;
}
$A(element.childNodes).each(function(node) {effect(node, options)});
return;
}

// It's only executed when node is a text node.
options = new Hash({
color: '#ffffff',
size: 4,
repeat: true,
interval: 70
}).merge(options);

// The all characters is replaced because accessing an innerHTML property is pretty slow.
var parent = element.parentNode;
parent.innerHTML = $A(element.nodeValue).map(function(text) {
return ['<span style="color: ', this.color, '">', text, '</span>'].join('');
}).join('');

var self = this, started = false;
var turnOn = function(node) {node.style.color = options.color};
var turnOff = function(node) {node.style.color = self.color};
var restore = function() {parent.innerHTML = self.innerHTML};
var startTurnOffEffect = function(callback) {
started = true;
$A(parent.childNodes).inject(0, function(delay, node, idx) {
with({idx: idx}) {
setTimeout(function() {
turnOff(node);
if(callback['onEnd'] && idx == parent.childNodes.length - 1) callback['onEnd']();
}, delay);
}
return delay + options.interval;
});
};
// Start the effect
$A(parent.childNodes).inject(0, function(delay, node, idx) {
setTimeout(function() {
turnOn(node);
if(!started && idx > options.size) {
startTurnOffEffect({onEnd: function() {
restore();
if(options.repeat) effect(parent, options);
}});
}
}, delay);
return delay + options.interval;
});
};



iPhone or iPod touchユーザなら一目瞭然ですが、iPhone / iPod touchのエフェクトはより滑らか。またの機会に、イルミネーションをより滑らか(2〜3ステップで明るくなるよう)にするかもしれません。

Thursday, December 06, 2007

[EC2] Debian Etch AMIの作成

基本的には、こちらの手順通り実行するだけですが、/usr/lib/site_ruby/aes/amiutil/image.rbを少し編集しないとAMIが起動しません。
HOWTO Building a self-bundling Debian AMI

作成したAMI上でAMIのバンドルを可能にするためec2-ami-toolsをインストールする。


# cd /usr/local/src
# wget http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.noarch.rpm
# alien --to-deb ec2-ami-tools.noarch.rpm
# dpkg -i ec2-ami-tools_1.3-15587_all.deb


/usr/lib/site_ruby/aes/amiutil/image.rbに対するパッチ

*** image.rb 2007-11-20 11:06:08.000000000 +0000
--- /usr/lib/site_ruby/aes/amiutil/image.rb 2007-12-06 02:59:31.000000000 +0000
***************
*** 172,183 ****
--- 172,190 ----
# Make device nodes. dev_dir = IMG_MNT + '/dev'
Dir.mkdir( dev_dir )
+
+ # Make mount point
+ FileUtils.mkdir_p(IMG_MNT + '/lib/init/rw')
+
# MAKEDEV is incredibly variable across distros, so use mknod directly.
exec("mknod #{dev_dir}/null c 1 3")
exec("mknod #{dev_dir}/zero c 1 5")
exec("mknod #{dev_dir}/tty c 5 0")
exec("mknod #{dev_dir}/console c 5 1")
exec("ln -s null #{dev_dir}/X0R")
+
+ # Make special device
+ exec("cd #{dev_dir} && /sbin/MAKEDEV std && /sbin/MAKEDEV generic")
end

#----------------------------------------------------------------------------#


Railsで使用するなら、Rakefileを作成しておきます。
コマンド例

% rake ec2:images
% rake ec2:run id=i-38f7397
% rake ec2:instances
% rake ec2:terminate id=i-38f7397
% rake ec2:console_output id=i-38f7397
% rake ec2:register manifest=my-images/debian_etch_Dec06.manifest.xml
% rake ec2:deregister id=ami-33js73
% rake ec2:login user=Gabriel


#{RAILS_ROOT}/lib/tasks/ec2.rake

# cavii, Inc
# Tasks for EC2
require 'env'
require 'erb'

namespace :ec2 do
@config ||= YAML::load(ERB.new(IO.read("#{RAILS_ROOT}/config/aws.yml")).result)

desc <<-EOH
Describe the available images to launch. You can specify the owners of images.
e.g.) rake ec2:images [id=ami_id | --all]
EOH
task :images do
id = ENV['id']
system %{cd #{ENV['EC2_HOME']} && ./bin/ec2-describe-images #{id}}
end

desc <<-EOH
Describe the current state of the specified instances(s).
e.g.) rake ec2:instances [id=i-038592]
EOH
task :instances do
system %{cd #{ENV['EC2_HOME']} && ./bin/ec2-describe-instances #{ENV['id']}}
end

desc <<-EOH
Run an instance with ami_id. You can obtain ami_id using 'rake ec2:images'.
e.g.) rake ec2:run id=ami-3683a933
EOH
task :run do
begin
ENV['key'] ||= 'gsg-keypair'
system %{cd #{ENV['EC2_HOME']} && ./bin/ec2-run-instances #{ENV['id']} -k #{ENV['key']}}
rescue
puts %{Usage: rake ec2:run id=<AMI ID>}
end
end

desc <<-EOH
Terminate the specified instance.
e.g.) rake ec2:run id=i-5ab44333
EOH
task :terminate do
begin
system %{cd #{ENV['EC2_HOME']} && ./bin/ec2-terminate-instances #{ENV['id']}}
rescue
puts "Usage: rake ec2:run id=<AMI INSTANCE ID>"
end
end

desc <<-EOH
Registers the Amazon Machine Image, and you should specify the manifest file.
e.g.) rake ec2:register manifest=mybucket/image.manifest.xml
EOH
task :register do
begin
raise unless ENV['manifest']
system %{cd #{ENV['EC2_HOME']} && ./bin/ec2-register #{ENV['manifest']}}
rescue
puts "Usage: rake ec2:register manifest=mybucket/image.manifest.xml"
end
end

desc <<-EOH
Deregister the amazon machine image.
EOH
task :deregister do
system %{cd #{ENV['EC2_HOME']} && ./bin/ec2-deregister #{ENV['id']}}
end

desc <<-EOH
Show the console output, and you should specify instance id.
EOH
task :console_output do
system %{cd #{ENV['EC2_HOME']} && ./bin/ec2-get-console-output #{ENV['id']}}
end

task :login do
user = ENV['user']? ENV['user'] : 'root'
system %{ssh -i #{@config['ec2_id_rsa']} #{user}@#{@config['primary_instance_url']}}
end

end



config/aws.yml

aws_access_key: 'XXXXXXXXXXXXXXXXX'
aws_secret_access_key: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
aws_account: 'XXXX-XXXX-XXXX'
image_bucket: "mybucket"
ec2_id_rsa: '~/ec2/id_rsa-gsg-keypair'
ec2_keypair_name: "gsg-keypair"
primary_instance_url: 'ec2-62-222-21-118.compute-1.amazonaws.com'
options:
use_ssl: true

Wednesday, November 14, 2007

[Javascript] 64ビット演算のエミュレート

「最近記事書いてないね」って突っ込まれる。
プログラムは、たくさん書いているのですが。

ECMA-262で定義されている通り、ビット演算子は符号付き32ビット整数しか扱えない。Number型は倍精度フォーマット IEEE 754型なので64ビットまで扱えるので、結果が2の31乗以上の値になるビット演算子を扱う時には2の32乗を加算して、補正してやる必要がある。

※ Math.pow関数など、2の53乗以上の数は誤差が出るので注意


console.log((Math.pow(2, 53)-1).toString(2)); // => 11111111111111111111111111111111111111111111111111111
console.log((Math.pow(2, 54)-1).toString(2)); // => 1000000000000000000000000000000000000000000000000000000


64ビット演算のエミュレート。
ECMAScript4にならないと演算子のオーバーロードができないので、関数呼び出しでエミュレートする。関数の呼び出しコストについては後述。


var ASSERT = function() { // {{{
var arg = new Array(arguments.length);
for(var i=0,len=arg.length;i<len;++i)i>[i] = arguments[i];
if('console' in window && 'assert' in console) { // use Assert of Firebug
console.assert(arg.shift(), arg.pop());
} else {
if(!arg.shift()) throw(arg.pop());
}
}; // }}}


// 4294967296 is Math.pow(2, 32);
Number.prototype.bitwiseAnd = function(rhs) {
return (this & rhs > -1)? this & rhs : this & rhs + 4294967296;
};
Number.prototype.bitwiseOr = function(rhs) {
return ((this | rhs) > -1)? this | rhs : (this | rhs) + 4294967296;
};
Number.prototype.bitwiseNot = function() {
return ((~this) > -1)? ~this : (~this) + 4294967296;
};
Number.prototype.bitwiseXor = function(rhs) {
return ((this ^ rhs) > -1)? this : (this ^ rhs) + 4294967296;
};

ASSERT(0xffffffff.bitwiseAnd(0xffffffff) == 1, 'bitwiseAnd: 0xffffffff & 0xffffffff is equal 4294967295.');
ASSERT(-0x1.bitwiseAnd(0xffffffff) == -1, 'bitwiseAnd: -0x1 & 0xffffffff is equal -1.');
ASSERT(0x1.bitwiseOr(0xffffffff) == 4294967295, 'bitwiseOr: 0x1 | 0xffffffff is equal 4294967295.');
ASSERT(-0x1.bitwiseOr(0xffffffff) == -4294967295, 'bitwiseOr: -0x1 | 0xffffffff is equal 4294967295.');
ASSERT(0x1.bitwiseXor(0xffffffff) == 4294967294, 'bitwiseXor: 0x1 ^ 0xffffffff is equal 4294967294.');
ASSERT(-0x1.bitwiseXor(0xffffffff) == -4294967294, 'bitwiseXor: 0x1 ^ 0xffffffff is equal -4294967294.');
ASSERT((~1) == -2, 'signed 32 bitwiseNot: (~1) is equal -2');
ASSERT(0x1.bitwiseNot() == 4294967294, 'bitwiseNot: 0x1 ^ 0xffffffff is equal 4294967294.');
ASSERT(-0x1.bitwiseNot() == -4294967294, 'bitwiseNot: 0x1 ^ 0xffffffff is equal -4294967294.');

delete ASSERT;



命令数を減らして高速化したいというニーズから、ビット演算を使用する事が多いかと思うので、上記の場合、関数呼び出しのコストが余分にかかる事に気をつける必要があります。インライン展開したコードに比べて、関数が呼び出されるコードでは、約2.5倍程遅くなるので必要に応じてインライン展開してください。

AND演算を100000回実行した結果
インライン: 387ms
bitwiseAnd関数: 991ms

Sunday, November 11, 2007

[Javascript] 64bit Number型を上位32bitと下位32bitに分割

JavascriptのNumber型は、倍精度64bitフォーマット IEEE 754型(double-precision 64-bit format IEEE 754 values)です。Javascriptでは、64ビット演算はできないので、上位32ビットと下位32ビットにわけて計算しないといけません。また、Math.pow(2, 55)など大きな数を扱う時には誤差も発生するので別途内部表現を設けて対応しないといけません。

パフォーマンスの犠牲を払っていますが、
上位32ビットと下位32ビットに変換します。


Number.prototype.rdHi = function() {
var b = this.toString(2);
return parseInt(b.slice(0,b.length-32), 2) || 0x0;
};
Number.prototype.rdLo = function() {
var b = this.toString(2);
return parseInt(b.slice(Math.max(b.length-32, 0), b.length), 2);
};
console.log(0x03ffffffff.rdHi() & 0xffffffff); // => 3
console.log(0xffffffffff.rdHi() & 0xffffffff); // => 255
console.log(0x01ffffffff.rdLo()); // => 4294967295
console.log(0x01ffffffff.rdLo() & 0xffffffff); // => -1


注意しなければいけないのは、最上位ビットは符号ビットだと言う事。最後の結果は、0xffffffffではなく、-1になります。32bit値を何かしらのフラグとして利用する場合は、最上位ビットを考慮したロジックを組む必要があります。

Number型(64bit)を利用する場合速度とメモリ消費量のトレードオフとなります。
例を挙げれば、ボードゲームを実装する際のデータ構造として一般的なBit Boardならば、次のどちらかの戦略をとる事になるかもしれません。
  • 64bit値を下位32bitと上位32bitの二要素として配列に持つ
  • 64bit変数で保持して、実行時に上記のメソッドを適用する。

一つ目は、メモリ消費量が多くなりますが配列から何かしらの操作を実行する時のコストは低くなり、二つ目はメモり消費量は減らせるが実行時のコストが大きくなります。

P.S
2の53乗以上のデータも扱えるように、倍精度でも誤差がでないNumber型を実装していたら似たようなライブラリがたくさんあったので中断。きちんと調べてから実装するべきだった。

Sunday, October 21, 2007

ハードウェアにおける第三の波


前回のエントリから2ヶ月以上が経過した。
先月は、世界最小のRIAプラットフォームとして紹介されているDLNA対応のDegital Photo Frameを作った。(写真は、naotakegymnanisium:mtよりお借りした。)














以下に書いた事柄を、「ハードウェアにおける第三の波」と定義した。

先日iPhoneのFirmwareを1.1.1にUpdateしたのだが、POBoxを搭載したキーボードによる日本語入力の対応やiTunes Music Storeや、カメラ撮影時のシャッター音など実に様々な機能が追加された。

これだけ豊富なアップデートは、「320x480のディスプレイとホームボタンのみ」というハードウェアの特性だからこそ可能になったのではないだろうか?日本の携帯のハードウェアでは、ソフトウェアだけでこれほど大規模なアップデートは不可能である。ハードウェアの形状やデザインがソフトウェアに多くの制限をかけており、新しいソフトウェアキーボードは追加できても、今の科学では新しい物理的なボタンをネットワーク経由で追加する事は不可能だからだ。

21世紀は、ハードウェアとソフトウェアの融合がキーワードだと思うが、このフィールドで勝負する時に忘れてはいけないのが次の点ではないだろうか。


  • ネットワークに接続可能なアドホクラシーなハードウェア
  • プロシュマーによるオープンイノベーション
  • ヒューマンフレンドリーなコミュニケーション



一つ目の「アドホクラシーなハードウェア」という言葉は、一言で言うならば、ソフトウェアによる機能追加等を最大限可能にするため、ハードウェアでの制限はなるべく取り除くという考え方である。ソフトウェアの更新により劇的な進化をユーザに感じてもらうには、ハードウェアは(外見的には)シンプルであるべきである。

二つ目のメッセージは、一つ目のキーワードを満たしたハードウェアが本当に価値を発揮するのは、多くのプロシューマー(生産消費者: ユーザでありながら創作活動を行う人々)を巻き込んだ時である。iPhoneに優れた多くのネイティブアプリが登場している事からも明白である。ソフトウェアによる更新が可能なハードウェアは、今後ますますオープンになっていくだろう。多くのプロシューマーを取り込めるプラットフォームを作った物が勝者となる。

三つ目は、機械と人間のコミュニケーションについてである。
技術は、「人と人とのコミュニケーションをより便利にしたい」という欲求と共に進化してきたが、今、劇的に変化が求められているのは、「人と機械のコミュニケーション」ではないだろうか?私がMacbookとコミュニケーションする手段は、キーボードによる入力が95%以上を占めている。内蔵されたiSlightによるコミュニケーションも可能であるが、カメラを使用するのは、SkypeやCamera, Videoオブジェクトを使用したFlashを作っているWebページぐらいである。キーボードはプログラミングのような文字入力に適した入力インタフェースであるかもしれないが、果たして多機能化されたPC上の全ての行動に最適な入力ユーザインタフェースだろうか?

故障していない限り、全てのPCはキーボード上のキーをタイプすればその入力を電気信号に変換し、アプリケーションに伝え適切な処理を行う。こちらがコミュニケーション方法を間違えれば、機械も全く同じように間違える。

もし、人間同士とのコミュニケーションならば、こちらが間違えたとしても、相手が頭の中のフィルターを通し適切な情報、もしくは自己に都合の良いように解釈する。そして指摘すれば学習する。これから重要になると考えている技術の一つは、人間がアバウトに伝えても人間が伝えたい事を解釈し機械が補足してくれる仕組みとユーザインタフェースにおける"学習"である。


PC一つで様々なニーズを満たせるようなった。様々なアプリケーションが開発されチップの性能も格段に進化した。そして、今後はそれらの技術が"したい事"に特化したモノへと還元されていくのではないだろうか?


これらの事が実現する世界では、ますます事象同士のつながりや相互作用をとらえ、どういう組み合わせに可能性があるのか?を実世界の経験をベースに考えていかなければならない。そして、そんなアツい思いを実現する事に夢中になれるクレイジーな連中と新しい会社を立ち上げる事にした。

Wednesday, August 01, 2007

UIE SDK 2.2はWYSIWYGで各種デバイス向けアプリケーションを作成できる

展示会でデモが公開されていたので知っている人もいるかと思うが、UIEvolution社が開発しているUIE SDK 2.2(仮)が興味深い。様々なデバイス向けのアプリケーションをGUIで簡単に作成できるツールで、xcodeのInterface Builderのような機能を提供する。

まだUJML暦1ヶ月だが、UJMLを学ぶと同時にUIE SDK 2.2をドッグフードしているのでUIE SDK 2.2の紹介と課題を書くことにした。

※ UIE SDK 2.2は2007/08/01現在外部に対して公開されていない。リリース目指して鋭意開発中である。写真は近日社内向けに公開されたDaily Buildの画像であり外部にリリースされるUIE SDK 2.2とは異なる場合があります。

現在リリースされているUIE SDK 2.1.1はUIEvolution Developer :: Developer - UIER SDKよりダウンロードできる。

UIE SDK 2.2の画面キャプチャ


UIE SDK 2.2での開発プロセスは、Assemblyと呼ばれるコンポーネントを作成しそれらをつなぎ合わせることでアプリケーションの開発を進める。Assemblyは画面上に表示されるVisual Assemblyとと画面上に表示されないData Assemblyに大分される。前者は特定のユーザインタフェースを実装するのに対して、後者はRSS Feedを取得しVisual Assemblyにデータを渡すといったData Providerとしての役割を持つ。

Assemblyの接続は結線図で表され、一つのAssemblyは入力ポートと出力ポートを持つ。



UIE SDK 2.2にはCommon Assemblyとして多数のAssemblyが用意されている。例を挙げれば、Function Keyが押された時に押されたことを通知するだけのAssembly, RSSを取得してパースするData Assembly, テキストフィールドAssembly, スクロール可能なリストデータを表示するAssemblyなどがある。



Assemblyの構造は次のようになっている。

- images
-- アイコン画像など
- resources
--ポートにマウスオーバした際に表示するツールチップ中のコメントを定義するファイル
- assembly.xml(ポートやAssemblyに設定するプロパティの定義)
- MANIFEST.MF (依存関係やAssemblyのタイプの定義)
- YOUR_ASSEMBLY.ujml


サンプルの画像を示すために、Picker Assembly(ユーザが決められた値の中から任意の値を選択できる)を作成し時計の設定画面ぽいUIを作成してキャプチャした。Pickerは4枚の画像(次の値と以前の値を選択する矢印画像の選択時と非選択時)を設定できる。また、このAssemblyではBackgroundの色や文字の色(フォーカス時・非フォーカス時)、Pickerの方向(垂直 or 水平)のプロパティを選択できるように実装した。





Picker Assemblyで指定可能なプロパティ


Picker Assemblyは現在の値をプロパティで指定した、文字で接続して次のPickerに渡すことができる。ここでは最終的にTextField Assemblyに接続して画面上に表示している。



画面上に描画するデバイスに依存したコンポーネント部分を実装すれば新しいデバイスでもUIE SDK 2.2で作成したアプリケーションが動作する。移植の容易性が大きなメリットであると思う。

開発プロセス
DeveloperとDesignerの分担だが、現在次のようなアプローチを試している。

1. Designerが画面イメージやUIを作成する。
2. DeveloperがUIを実現するAssemblyの設計と開発を行なう。
3. Designerから画像を貰い、作成したAssemblyと合わせて実際に動作するアプリケーションを作成する。
4. DesignerがUIE SDK 2.2を使用して、位置の調整や画像の差し替えなどの最終調整と動作確認を行なう。

課題
UIE SDK 2.2ではデータやアクションのやり取りを結線図で表すアプローチを取っているが、どうしても複雑になりがちである。以下のキャプチャはリストの操作と4方向のキーを受け取るだけの簡単なUIではあるが結線はそれなりに複雑だ。xcodeのInterface Builderでは結線は(常時)表示せずプロパティでコンポーネントの接続を管理しているがどちらがわかりやすいかと言えば微妙な所だ。(xcodeも癖があると思う。)



Assemblyをどの程度の粒度で作成するのがよいかという事に関しても明確なガイドラインは現在存在しない。細かくしすぎるとパフォーマンスが犠牲になるので、この辺はドッグフードを繰り返しながら良いツールになるように洗練させていくしかない。

PS.
一つのツールを"当たり前"に使いやすくするというのがどれだけ大変な事かがよくわかった。

Saturday, July 28, 2007

World Wide Living とHardware Mashupの世界

こんな世界を想像してほしい。

自宅のリビングにあるテレビが「FULL HDのディスプレイを持ち、5.1chサラウンドシステムのスピーカーに接続され、外部のネットワークに接続しておりWWWにアクセス可能である。」とブロードキャストする。5.1chサラウンドシステムのスピーカーは、「各スピーカの場所と映画やスポーツの再生に特化している」とブロードキャストする。デジタルカメラは、「700万画素のカメラを持ち、高速で精度の高い顔検出機能を持つ」とブロードキャストする。炊飯器は、「本炭釜を持ち、低機能な液晶ディスプレイを持っている。」とブロードキャストする。自宅の自慢のスピーカーは「フルレンジスピーカーであり、低音から高音まで豊かな再生が可能で、特にJazzやClassicの再生に優れている」とブロードキャストする。


私は全ての機器がブロードキャストし急速に発展する無線技術によってプライベートなネットワークに接続すべきだと考える。ネットワークに参加する事によりネットワーク内に存在するハードウェアが持つ機能レベルでのマッシュアップが可能になる。その次にはハードウェアとハードウェアのマッシュアップが存在するかもしれない。そしてこれは自宅のネットワークに限った事ではないと思う。人間自身、そしてその人間が身につけるガジェットがオンデマンドにネットワークに参加する事で、そのネットワークが提供するサービスを楽しむ事ができる。

WWL(World Wide Living)はLivingが外出先から楽しめるだけでなく、どこにいっても自分のLivingのように自分のためのサービスを受けれる。そんな気持ちを込めている。

デジタルカメラで撮影した画像がテレビでスライドショーで見れるデモをよく見かける。一昔前〜現在は、SDカードなどの外部記憶装置を介してデータのやり取りを行ったが、DLNAではこれらのデータや機能はデバイスが自動的に発見して再生する事ができる。

DLNAが完全に標準となっているわけでもない。Intelが進めるViiVや仕様の策定が遅いからと各ベンダーが独自実装を進めるような流れも見られる。また、PS3のDLNA Client機能とWindows Media Player 11に搭載されているDLNAを使用してスライドショーを実践してみたが、XMBのPhotoの下にまたPhoto, Movie, MusicとありPhotoから遷移した場合は、Photo以外のメニュー(Movie, Music)を選んでも何も表示されない事や、Windows側の設定がふつうの人に取ってはまだまだ難しいなど洗練されていない。(これはDLNAの仕様を忠実に画面上に表示しているだけだと思われる。)

携帯電話やPDAの世界ではオープンな規格やソフトウェアを取り込もうという流れが盛んである。LiMo Foundation, Betavine, Qtopia, OpenMokoなどが主である。

家電の世界ではまだまだメーカーの独自規格が多い。
また国内で言えば革新的な技術を持っていても、SONYなどの大メーカーは採用する事はまず無いだろう。しかし、メーカーの独自規格が乱立する世界からオープンで開かれた世界に切り崩していかなければならない。なぜならば、チップの小型化や省電力化、無線技術の急速な発展といったテクノロジーの発展をどう人々のライフスタイルに結びつけるかが重要になるからだ。技術中心のアプローチではなく、技術と人々の生活を深くした上で経験デザインが行われる必要がある。これらは必ずしもメーカーだけが得意な事ではない。豊かな創造力と高い技術力を持つ人達が切り込んでいかなければならない。

もっとも、私は楽観主義者であるし困難な側面を考えるよりは、何からできるかを考える方が好きだ。

豊かなライフスタイルや技術革新を想像するだけなら簡単である。どのようにインプリメンテーションするかが最も重要だ。これからますます、高い技術力と共に技術をイノベーションに結びつけるテクノロジストとしての才能が問われる。幸いな事にテクノロジストとしての才能は、人の何倍も知的好奇心を持ち努力し続けることで身につけることができる。そして、イノベーションは自分が深い知識を持つ分野と全く違う分野のキスで起こる可能性を秘めている。

最近、MADE IN JAPANと盛田昭夫語録を読んだ。
当時最高の研究所であったAT&Tのベル研究所ですら開発をあきらめていた高周波数で動作するトランジスタの開発、テープレコーダーの開発、そして既存の技術を組み合わせて音楽を聞くという行為を再発明したウォークマン。近頃は技術が成熟したことで技術のメインストリームが見えにくくなっているという話を聞くが、それは自分の目が節穴なだけだ。人間は技術的困難に挑戦する事を未来栄光欲するだろうし、それを乗り越えた時の喜びはひとしおである。

私は一般家庭からPCという物はなくなると思っている。
WWWはなくならないが、PC中のブラウザを立ち上げてインターネットを楽しむなど笑い話になる日がくるだろう。電気や水道のように、先進的なチップによる3D表示やマルチタッチパネルなど実世界インタフェースがすごい勢いで取り込まれることで情報技術は日常生活に浸透したものとなるだろう。

アイデア一つ、面白いWEBアプリケーションを作ってPVを集める事などイノベーションでも何でも無い。それなら個人でやればいい。MADE IN JAPANを読めば、時代を切り開いた実業家でありテクノロジスト Mr. Akio Moritaの哲学に触れる事ができる。世界中にSONYブランドを根付かせた盛田氏の哲学には深く感動した。



Saturday, July 14, 2007

計算機と右脳をつなぐ脳梁のイノベーションでクオリアの入力を可能に

UIEJには毎日35分かけて徒歩で通勤しているが、好きな音楽を聞きながらCoolなモノへの想像を膨らませるのが通勤途中の楽しみの一つだ。読んだ本の影響もあり、最近は脳に関する想像が多い。

私は脳神経科学についての知識は皆無だし、認知心理学についてもあまり理解していないが最近読んだいくつかの本から考えた事をまとめてみようと思う。あくまで妄想。

1950年代には既にJohn von Neumannが提唱していたニューラルネットワーク。50年前とは比較にならないぐらい技術が進歩した今でも難しい事が多いが様々なアプローチで真剣に進めていく価値はある。(脳をエミューレトするのではなく計算機と人間間の差を減らすとか)


左脳(== 計算機)と脳(右脳)をつなぐ脳梁(ヒューマンユーザーインタフェース)にイノベーションが求められる。
今、私が最も関心を持っているテーマがこれである。
様々な技術革新が行われ計算機のハードウェアは急速に進歩した。ウォークマン•iPodといったウェアラブルコンピューターの息吹も見られる。しかしインターネットへの入り口はブラウザがほとんどだし、入力でバイスは文字を入力する事で”やりたい事”を実現する物がほとんどである。

Wiiリモコン, DoCoMo 904シリーズ, iPhone, Surfaceのように実世界インタフェースを取り組む動きが盛んだ。iPhoneでは二指を広げると拡大、狭めると縮小という単純な動作に徹している。パターンを増やすとユーザーは混乱するので徹底したシンプルさ(例えば、Webkitから二指のイベントを扱う事はできない)は素晴らしいと思う。

10年後には"レゴのようなモノ"でマイホームの設計からデザインを顧客自身が行うようになるかもしれない。デザインやパターンはインターネットでダウンロードして利用できる。レゴの表面はダウンロードしたデータ中で指定された色に基づき発光する。そしてその模型をまたデータ化して不動産業者に送信し工事が始まる。

しかし、実世界インタフェースを取り込む入力デバイスの実用化にはまだまだイノベーションが必要だ。Newral Interfaceのようなインプラントで脳内にチップとトランスポーターを埋め込む話は大変面白いが、私はDARPAの研究者ではないし現在の興味深い技術をとっても簡単に利用できる製品を作りたい。2050年頃に臨床実験が可能になるかもしれない話では駄目である。

つまむ, ひっぱる, 興奮する, 落ち着く, 騒がしい, 静かだ, いい香りだ, など様々な実世界の概念が入力されて利用できるように入力デバイスのイノベーション(技術革新)が求められる。

そしてそれと同じぐらい大事なのが、左脳(計算機)が右脳の直感やアフォーダンスを予測しシミュレートすることである。東京大学の五十嵐健夫氏の研究デモは計算機が人間のアフォーダンスを予測し、人間にとっては当たり前の事だが計算機に取っては当たり前でない事をデータ化する試みだ。

http://130.158.75.1:443/esper2007/igarashi.wmv

つまり、クオリアをデータとして取り込みたいのである。


計算機と脳を含めた並列コンピューティング。
近い将来誰でもサヴァンになれる日がくるかもしれない。

計算機と人間は明らかに思考ロジックが異なる。物事を部分ではなく全体で捉えるのが得意な脳に対して、計算機は0と1という単純な2値の膨大な組み合わせで全体を精確な部分で構成する事に長けている。

近年世界最強のチェス専用スーパーコンピューターであるディープ・ブルーが人類を打ち負かした話は有名であるが、駒の動きを複雑化させたアリアマというボードゲームでは計算量が膨大になるため(まだしばらくは)人類が有利だと言われている。興味深いのは駒の動きの複雑度が増すにつれて、指数関数的に計算量が増大する計算機とは異なり人間はそのような事が無い。

私は次のように考える。
計算や記憶など計算機が得意とする領域は、脳の外部に置かれることになる。そして人間の想像力を司ると言われる右脳がデータを要求した時に、「いつ」「どこでも」「好きな時に」外部に蓄積されたデータを取り出したり、必要に応じて加工できるようになる。その際に問題となるのは最初に書いた計算機 == 左脳と右脳をつなぐ脳梁(==Human User Interface)での情報損失である。

実際にNational Science Foundation(アメリカ科学財団)は、世界の全人口の記憶の総量は1千2百ペタバイトであり、脳の外部に蓄積されている情報を合わせると1万2千ペタバイトになると結論づける。(※1) 外部に蓄積される情報量はますます増えると容易に予測できる。

脳を含めた巨大で膨大な並列ネットワークを個人が手に入れようとしている。しかしそれには計算機と脳をつなぐHuman User Interfaceのイノベーションが必要である。Human User Interface(脳梁)でデータの損失、つまり思った通りのデータがCRUDできないとかあり得ないのだ。

コンピューター関連テクノロジーの発展が人類の左脳の進化に影響を与える。
コンピューターの登場により数学界における問題解決のアプローチは大きく変わった。何百年という期間の間証明できなかった問題に対して、スーパーコンピューターの力を利用するごりおしの力技があらわれたからだ。ケプラー予想が有名である。この傾向を危惧する数学者も少なくない。

直感や意識など高次の抽象的な感覚がイノベーションにより登場した入力デバイスから計算機にINPUTできるようになれば、データ処理は計算機が行い人は感覚的な高次の概念を左脳(計算機)に入力する事に徹する事になる。人間はそれを意識する必要も無いし、人類の進化という長い時間軸で考えるならコンピューターの発展はホモサピエンスの左脳を萎縮させるのではないだろうか?


「1は背が高くてとっても光り輝いている」「4は内気でもの静か」と数字に色だけでなく様々な共感覚を持つダニエルのヒューマンドキュメンタリー「ブレインマン」をとても見たくなった。


CHANGELOG
*2007/07/15 右脳と左脳をつなぐ組織は脳幹ではなく、脳梁だったので修正。


参考文献


1. How Much Information? 2003
2. ぼくには数字が風景に見える, ダニエル•タメット 訳 古屋美登里
3. 超人類へ! バイオとサイボーグ技術がひらく衝撃の近未来社会




脳内晒し

流行っているかどうかは知らないが、脳内メーカー晒し。



現状に満足する事は退屈だし、ダサイし、大嫌い(一般論ではなく私の中のドグマにおいて)だが、一体どれだけ欲求不満なんだ。でも技術の発展は目覚ましいのに、インターネットは未だにブラウザで利用するものだし、人間と計算機間のインタフェースはキーボードが主流だし、WiTricityの研究はまだ始まったばかりだし、本当に欲求不満だらけだ。

負というのが気に食わないが、これは正数に対する負の数の事にしておこう。
だって、unsigned型だけではプログラム書けないし。

これを見るととても強欲に見えるが「30歳で1500万貰って高級車乗り回して、毎日夜遊びして酒飲んで、40歳でアーリーリタイア」なんて私欲をわずかに肥やすプランはほとんど考えた事がないし、そんな物は本当に退屈なプランだ。(好きで楽しくてたまらない事をやってるのになぜリタイアなのか理解できない。)

で、食欲は"食"というマークがあったし、性欲は"H"だろう。金銭欲は"金"があった。ではこの欲は一体何なのだ?まじめに考えるに値しないテーマかもしれないがとても気になる。

お金に関して言えば、例えば、「実世界インタフェースをどう計算機に取り入れられるか」に関して興味があるのでSurfaceを買ってHackしたいと思った時に買えるだけあればいいし、iPhoneを買って分解してぶっ壊してしまってももう一台買えるぐらいあればいい。東大の五十嵐先生が行われている、実世界の思考やアフォーダンスを計算機がシミューレトしてくれるような大変興味深い研究を実装したデバイスのプロトタイプを開発するぐらいの資金があればいい。$10Bぐらいあれば十分だ。(言ってる事が支離滅裂に聞こえるかもしれないが、正直な所金銭欲なんてそんな程度のアバウトさだ。)

ちなみにUIEJの仲間の人の頭の中はこんなだった。
http://naotake.fs-output.com/2007/07/ver.html


> 誰か説明できる人いる?
だって「中学生の思春期のまっただ中で頭の発育が停止」してますよ。
(理解していると思いますけど、私なりの賛辞なので怒らないでください)

下記の本はMicrosoftのInternetExplolerやOutlookの開発者による近未来のバイオテクノロジーやNeural Interfaceに関する本で大変面白い。SFチックなタイトルだが、本の内容はDARPAによる最新の研究事項や実例を示しながら書かれているとても真面目な本である。

人間の飽くなき向上心はとどまる事を知らないし人類は進化する方向を自分達でコントロールできる所まで進化したのだ。遠い将来、現在のホモサピエンスをルートとして多くの派生種が生まれる事は間違いない。読むだけで何とも楽しくなり、想像力をかき立てる本である。

Friday, June 29, 2007

心に残る名言

こんな事を言われた。

「いやぁ。古谷楽人、ほんと••••変態だわ。」

「馬鹿だよね」とか「頭おかしいよね」とか「ヒトラー」とか言われた事は今まであったが「変態」と言われたのは初めてだ。本当に心に残る賛辞である。ど変態目指して頑張る。

Friday, June 01, 2007

東京大学 先端科学技術研究センター キャンパス公開

キャンパス公開 2007

東京大学 先端科学技術研究センターのオープンキャンパスが明日までやってます。
本日行ってきましたが、興味深い基礎研究がたくさんありました。

基礎研究の動向を注意深く観察することは重要です。
資料もたくさん貰えるので行く価値は十分にあります。値段も無料。

Thursday, May 31, 2007

全く、お前の頭はイカれてるぜ!

  • MicrosoftによるaQuantiveの買収価格は$6B(7292.17307億円)
  • EbayによるSkypeの買収価格は$2.6B(3159.94166億円)。
  • GoogleによるYoutubeの買収価格は$1.65B(2005.34759億円)

Microsoftが買収したaQuantiveは時価総額が$2.8B 2006年度の収入高は$442M 純利益は約$54Mだが2倍のプレミアムがつき$6Bでの全額現金による買収となった。(現金による買収と株式交換による買収のメリット・デメリットや株主や市場への影響についてはバフェットからの手紙が勉強になる。)GoogleのDoubleclickの買収に慌てたYahoo!のRightMediaの買収に後を追った形ではあるが大きな買収額だ。


上記の例はアメリカンドリームだ。
ジャパニーズドリーム?なんだそれは?新興市場に上場することか?

日本がSkypeを作れない事が納得できない。
ここ最近の私の興味はこれに尽きる。Skypeとういのはあくまでメタファだが、グローバル戦略をしっかりと持ち自分達が素晴らしいと狂信するサービスを初期段階からグローバルで展開していくことがなぜできないのか。グローバルな展開を進めるとしても日本でヒットした後どうするかを考え、「(WEBアプリなら)Gettextで国際化してはい終わり。」と考えているところも多いはずだ。

動画関連にしてもP2Pにしても携帯関連にしても技術は日本にはある。
ただ、グローバルでサービスを展開するのが下手だ。iModeも失敗している。


海外の企業による日本のIT系企業の買収話はあまり聞かない。

UIEJで働くMicrosoftのIE TeamでProgram Mannagerを勤めた人とランチのときに話をした。
日本のマーケットは大きいしヒットすればそこそこの収益が出る。日本内でヒットするところをゴールに設定することで目的は達成される。グローバル戦略のプロを雇い初期からグローバル展開するサービスなどほとんどない。グローバル戦略はまるでおまけのような扱いだ。

しかし、先日の判決(PDF)を踏まえるなら、GoogleもYoutubeもSkypeさえ国内からは生まれないかもしれない。

日本人の国民性も大きく影響するようだ。
シリコンバレーのJTPAのような日本人ネットワークにみるように、インド人・中国人・韓国人と比べて日本人は世界でも日本人コミュニティなどに集結することが多いと言う。

だからこそ私は、何人だとか関係無しに実力一本・頭脳一つで戦う中島聡のような人間に惹かれるのかもしれない。

日本の企業は契約社会でもなくアメリカほど資本主義が徹底しているわけではない。組織の協調性や仲間意識などでバ ランスよく成り立っている。企業間でお互いの顔色を伺う。相手が動けば、合わせて動く。外部のテクノロジーを買収して自社に吸収したりチーム丸ごと引き抜くよりも、余計なコストをかけて”自社で同じ技術を作りたが る"ことが日本企業には良く見られると言う話を聞いた。


マーケティングとブランティング

多くのITベンチャーでは海外におけるマーケティング手法も確立していないしノウハウもない。
友達や近い人間だけで立ち上げる家族経営的な日本企業のベンチャーにおいてグローバル戦略のプロフェッショナルはいない。VCのアドバイスを受けることはあっても、自社に足りない部分を補足するため外部のプロフェッショナルを迎えいれるという文化は少ない。

もちろんMicrosoftなどの大企業においてもBtoC事業のマーケティングはまだ模索しているという。
MSに対して痛烈な意見を書くMicrosoft社員のアルファブロガーに自社の新プロダクトの記事を書いてもらうマーケティングもしているようだ。BtoCのマーケティングにおいてはAppleが郡を抜いている感がある。


ベンチャー
ITベンチャーに良く見るビジネスモデルは、SIerで最低限の収益を稼ぎつつ自社サービスをこつこつ開発するというものや既に確立しているサービスの隙間を狙いニッチな市場を狙い車輪の再開発を行うものだ。

プログラマの私はこんなものは全くつまらないと思う。
お小遣い稼ぎしながら片手間でpretty coolな物など作れない。本気でそのサービス・プロダクトの未来を信じ熱く語れるだけのビジョン(ここではお金を意味しない)を持ち魂をかけれるなら、SIerで小遣い稼ぎをして資金集めをするアプローチなど取らない。それは自信が無い事の裏返しでしかない。そのアイデアを信じ明確なプランを持ち何時間でも熱く話せるなら自分達にとって優位となるキャピタルを選ぶ立場に立つだろう。無理ならそのアイデアは捨てるまでだ。もしかすると、時代が早すぎたのかもしれない。

会社としては普通の物を作り出して収めてそこそこの収益をあげていく事が成功なのかもしれないが、一度しか無い人生においてそんなつまらない事に時間を費やすなんて馬鹿げている。絶対的に信じれる物を全力で作りダメだったなら、あきらめ次のものを生み出すまでだ。

世の中が便利になった副作用で大勢の人が数年先の未来を想像する力を無くし、現状に満足している。水平線の先が何があるのか見えない。

最も大事な事は未来を予測することではなく、技術動向を注意深くモニタリングすることだ。
流行に踊らされているだけでは、結果何も作ることができない。


面白いアイデア?
もし、自分のアイデアが「面白いアイデアだね」と周囲の人間が10人中8人以上評価するようなアイデアならそんなアイデアは腐っている。既に誰かがやっているし何ヶ月かかけて出来上がった成果物など最初のサービスを考えた当初の興奮は消え去り、たいして面白くもないものになる。

逆に頭がきれると自分が思う人間にそのアイデアを話して「お前は狂ってる。辞めとけ。」と言われた場合はチャンスかもしれない。Steve Jobsのような人間なら本当に素晴らしいアイデアならボロクソに罵るだろう。しかし彼の頭の中にはその狂ったアイデアがずっと残っているだろう。


「全く、お前は狂ってるぜ!」「お前の頭はイカれてる。」

あなたはそんな最高の賛辞を最近受けただろうか?
歴史は一人とか数人といった"常識に支配されないクレイジーな人間"によって切り開かれる。そして、マジョリティがそれに続き(市場・研究分野・技術)確立される。80年という短い生涯において歴史を変えるタイミングに出会えるのは1回もしくは2回程度だろう。チャンスを逃がしてはいけない。

Albert Einstein, (attributed) US (German-born) physicist (1879 - 1955)
Not everything that counts can be counted,and not everything that can be counted counts.
重要なものが捉えることが出来るすべてものの中にあるとは限らず、重要なものすべてを捉えられるわけでない。


この言葉はAppleのCM中でも現れるアインシュタインの有名な言葉だ。アインシュタインは「わたしには特別な才能はありません。 好奇心がとても強かっただけです」とも語っている。桁外れな好奇心こそフラット化する世界では重要な力だ。好奇心の芽を育てておけば歴史を変える事になるその瞬間を感じることができるだろう。大抵の人はそれに気づかない。

Think Different.
英文法的に間違っているとかJobsにとってはどうでもいいのだ。「だってこっちの方がCoolだろう?」彼のものさしは英文法ではない。言葉の持つカッコよさである。「常識にとらわれるな。常識を超えたところに解がある。目の前に見える解は本当の解ではない。」そんなメッセージをこの言葉に感じる。

Think Differenct (CM)
Here’s to the crazy ones.
The misfits.The rebels.The troublemakers.
The round pegs in the square holes.
The ones who see things differently.
They’re not fond of rules.
And they have no respect for the status quo.
You can praise them, disagree with them, quote them, disbelieve them, glorify or vilify them.
About the only thing you can’t do is ignore them.
Because they change things.
They push the human race forward.
While some see them as the crazy ones,
we see genius.
Because the people who are crazy enough to think
they can change the world, are the ones who do.

「常識を無視した好奇心」・「周囲からは狂ったように見える熱意・興味」
彼/彼女らはいつの時代も少数派だ。だから何だ?「いい人だった」とか「やさしい人だった」とかそんなものは100年後には残らない。10年後でさえ忘れ去られているだろう。

歴史に残るのは偉業である。
織田信長やチンギス・ハン・Steve Jobsがどんなに残忍だったり、冷酷かなんて対して興味をもたれない。モンゴルを統一したこと、Macを作ったこと。iPodを作ったこと。それらが歴史に残り語り継がれる。

このすばらしいCMを作ったAppleとジョン・フォン・ノイマンに感謝したい。

Monday, May 28, 2007

Comet勉強会第1回

Comet勉強会 第一回@ドリコムが開催された。
次回にもつながるいい勉強会だった。

でも、推測で話が進み、「こうだったらいいよねー」・「こうなってるに違いない」と話が進んでしまった。勉強不足でした。予習してませんでした。

Cometアプリってまだチャットだけ。
Cometキラーアプリそろそろ誰か出しておくか。

第二回も開いて欲しい!
以下次回ネタ。

  • AIO
  • MessageQueue
  • qbdm
  • SoftwareTransactionMemory
  • ・死んだ言語と言われてもC言語だよ。
  • epoll/kqueuで実装されていないイベントとかある


素晴らしいまとめおつです!
Comet勉強会#1発表資料 (ドリコム 瀧内さん)
Comet 勉強会に参加した (ドワンゴ 溝口さん)
rubyにFiberが... (スケールアウト yamazさん)
Comet 勉強会 (コミュニティエンジン 加藤さん)

ERTS読むぞ。

Friday, May 25, 2007

[Ruby] MemoizationのRubyによる高機能な実装Memoizeを作った。

Memoize

(src) memoize-0.1.0.tgz

gemだと敷居が高い事も十分に承知なのでページ下部にソースコードも晒しておきます。ご自由にご利用ください。

MemoizationはDonald Michieにより1968年に作られた言葉でその歴史は古い。最初の実装はCommon Lispのようだ。(参照: Wikipedia - Memoization) 既に十分に古い概念なのでRubyにもMemoizationの実装は存在するが随分と低機能で無駄なIOが発生するので高機能なMemoizationの実装をつくった。

Memoizationは簡単に言えば次のような機能である。

  • 関数呼び出しの結果を引数をキーにして保存する。
  • 元の関数をキャッシュが存在すればキャッシュを返しキャッシュが存在しない場合には値を導出してからキャッシュするように再定義する。


つまり、グローバル変数の値に実行結果が依存したり同じ引数を与えても実行の度に結果が変化するような副作用を伴う関数にはmemoizationを適用することはできない。また、通常のメソッドに余分なロジックを付加してメソッドを再定義するため極端にロジックの少ないメソッドでは逆に速度が遅くなる。

Memoizedするのに向いている関数(メソッド)は次のような性質を持つ。
  • 深い再帰を持つなど同じ引数で何度も同じ関数の呼び出し行われる。

  • 副作用を持たず、関数の実行コストが高いような関数。


Memoizationを実装するには、動的な関数定義を言語レベルでサポートしている必要がある。VHLLなRubyなら何の問題もない。今回実装したMemoizeは次のような機能をサポートしている。

  • memoizedデータを保存するストレージをプログラマが独自に実装できる。
  • トップレベル(main)だけでなく任意のスコープのメソッドをMemoizedできる。
  • 永続化させるためのmemoizeデータのファイルへの書き出しはプログラム終了時だけである。


TODO
  • スレッドセーフでない。


インストール

% wget http://rubyforge.org/frs/download.php/21051/memoize-0.1.0.gem
% sudo install Memoize-0.1.0.gem


Memoizedを使用したサンプルコード
Memoize.registerやMemoize.unmemoizeの第一引数に渡すselfはおまじないだと思ってください。トップレベルのmainへの参照を別のスコープから取得する方法がわからなかったのでこうなっています。知っている方がいたら教えていただけると助かります。

memoizeした関数を別の名前で参照したい際には、Memoize.registerの第三引数に:as => newmethodを指定する。


require 'memoize'
require 'benchmark'

def fib(n)
return 1 if n < 2
fib(n-1) + fib(n-2)
end

class Math2
def self.fib(n)
return 1 if n < 2
fib(n-1) + fib(n-2)
end
end

Benchmark.bm do |b|
b.report("fib(30): ") { fib(30) }
Memoize.register(self, 'fib')
b.report("fib(30) with memoize") { fib(30) }
b.report("fib(30) with memoize") { fib(30) }
Memoize.unmemoize(self, 'fib')
end

Benchmark.bm do |b|
Memoize.register(self, 'fib', :as => 'fastfib')
b.report("fastfib(30) with memoize") { fastfib(30) }
b.report("fastfib(30) with memoize") { fastfib(30) }
Memoize.unmemoize(self, 'fastfib')
end

Benchmark.bm do |b|
b.report("fib(30): ") { Math2.fib(30) }
Memoize.register(self, 'Math2.fib')
b.report("fib(30) with memoize") { Math2.fib(30) }
b.report("fib(30) with memoize") { Math2.fib(30) }
end


実行結果

% ruby test_memoize.rb
user system total real
fib(30): 7.060000 3.320000 10.380000 ( 10.376117)
fib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000043)
fib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000065)
user system total real
fastfib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000024)
fastfib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000023)
user system total real
fib(30): 6.380000 3.140000 9.520000 ( 9.520442)
fib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000029)
fib(30) with memoize 0.000000 0.000000 0.000000 ( 0.000024)
%


meoizedデータを保存するストレージの仕様
初めに書いたようにユーザ定義のストレージも使用できる。Memoize.registerに何も指定しない場合は、メモリ領域でデータを管理し、データを永続化させるために、プログラムの終了時にローカルのファイルシステムを使用してファイルとして書き込む。このファイルは/tmp以下に関数名をBase64エンコードしたファイル名.cacheで書き出される。

独自のストレージの実装
独自ストレージの実装はMemoize::Storableクラスを継承して以下の用に必要なメソッドを定義すればよい。詳しくは、lib/memoize.rb中のコメントを参照してください。MemCacheをストレージとして利用するクラスは次のように実装できる。

定義したストレージを使用するには、Memoize.registerの第三引数に:storeオプションを指定する。


class MemCacheStore < Memoize::Storable
attr_accessor :cache, :keys
def initialize(name)
@keys = []
@cache = MemCache.new 'localhost:11211', :namespace => 'rakuto.blogspot.com'
end

def get(key)
@cache.get(key)
end

def set(key, value)
@keys << key unless @keys.include?(key)
@cache.set(key, value)
end

def delete(key)
@cache.delete(key)
end

def delete_all
@keys.each { |key| @cache.delete(key) }
end
end


Memoize.register(self, 'fib', :store => MemCacheStore)
fib(30)


ソースコード: memoize.rb
memoize-0.1.0.tgz

# Memoize is implementation Memoization for Ruby, this techinique to make functions faster.
#
# == Caveats:
# * Do not memoize a function whose behaviou depends on program state.
# * Do not memoize a function with side effects.
# * Do not memoize a function that returns a data structure that is modified by it's caller.
#
# Copyright 2007 rakuto under MIT licence.
# Rakuto Furutani <rakuto at gmail.com>
#
# == See:
# Memoization - http://en.wikipedia.org/wiki/Memoization
#
module Memoize
VERSION = '0.1.0'
@@stores = {}

# Memoize::Storable class is abstract class. You must implement class which memoization data is saved.
# This module offer two storage class.
#
# * Memoize::MemoryStore -
# * Memoize::PStore - Persisten cache support.
#
class Storable
# {{{
attr_accessor :store

def initialize(name)
raise "Memoize::Strable don't instantiage for abstract class."
end

def set(key, value)
end

def get(key)
end

def update(key)
end

def delete(key)
end

def delete_all
end
# }}}
end

# MemoryStore is class which store memoization data to memory.
class MemoryStore < Memoize::Storable
# {{{
attr_accessor :store

def initialize
@store = {}
end

def get(key)
@store[key]
end

def set(key, value)
@store[key] = value
end

def delete(key)
@store.delete(key)
end

def delete_all
@store = {}
end
# }}}
end

# PStore is class which store memoization data with local file system.
# This store class be able to use persistent cache, because this class write out
# cache data on file when process is exit.
class PStore < Memoize::MemoryStore
# {{{
attr_accessor :store
PREFIX_SAVE_FILE_DIR = "/tmp"
SUFFIX_SAVE_FILE_EXT = ".cache"

def initialize(name)
@name = name
filename = encoded_filename(@name)
begin
if File.exists?(filename)
File.open(filename, "rb") do |io|
@store = Marshal.load(io)
end
else
@store = {}
end
rescue
@store = {}
end
# Write out cache
at_exit {
File.open("#{filename}", "wb+") do |io|
io.flock(File::LOCK_EX)
Marshal.dump(@store, io)
io.flock(File::LOCK_UN)
end
}
end

# Delete all memoized data and persisten cache fille
def delete_all
filename = encoded_filename(@name)
File.exists?(filename) && File.unlink(filename)
@store = {}
end

private
# Get the encoded file name
def encoded_filename(name)
File.join(PREFIX_SAVE_FILE_DIR, [name].pack('m').chomp+SUFFIX_SAVE_FILE_EXT)
end
# }}}
end

module_function
# Make memoized function.
#
# Parameters:
# * klass - You must surely specify 'self'. This parameter used to overwrite Toplevell function and
# register as toplevel function. I need to think better the means ;)
# * name - an memorized function name.
# * options - an specify options. <tt>:as</tt> is memorized function name, default is the same second arguments value.
# <tt>:store</tt> is specify class which store memorized data.
#
# == Memoized function examples
# def fib(n)
# return 1 if n < 2
# fib(n-1) + fib(n-2)
# end
#
# end
# Memoize.register(self, 'fib')
# fib(30) # => 1346269 fast
# fib(30) # => 1346269 very fast
#
# # Unmemorize
# Memoize.unmemoize(self, 'fib')
# fib(30) # => very slow
#
# == Memoized one Object methods
# class Math2
# def self.fib(N)
# return 1 if n < 2
# fib(n-1) + fib(n-2)
# end
# end
#
# Memoize.memorize(self, 'Math2.fib')
# Math.fib(30) # => fast
# Math.fib(30) # => very fast
# Memoize.unmemoize(self, 'Math2.fib')
#
# == Custom storage
# You can use own memoized data storage, in doing so you must implementaion following method
#
# Method/Arity:
# * initialize/1 - this is constructor
# * get/1 - an get the cache data with key
# * set/2 - an set the cache data with key and value
# * delete/1 - an delete the cache data with key
# * delete_all/0 - an delete all cache
#
# Following class is MemCache storage sample.
#
# # Memoized data storage using MemCache
# require 'memcache'
#
# class MemCacheStore < Memoize::Storable
# attr_accessor :cache, :keys
# def initialize(name)
# @keys = []
# @cache = MemCache.new 'localhost:11211', :namespace => 'rakuto.blogspot.com'
# end
#
# def get(key)
# @cache.get(key)
# end
#
# def set(key, value)
# @keys << key unless @keys.include?(key)
# @cache.set(key, value)
# end
#
# def delete(key)
# @cache.delete(key)
# end
#
# def delete_all
# @keys.each { |key| @cache.delete(key) }
# end
# end
#
# Memoize.register(self, 'fib', :store => MemCacheStore)
# fib(30) # => fast
# fib(30) # => fast
#
def register(klass, name, options={})
ns = name.split(/::|\./)
method, klass = ns.pop, (ns.empty? ? klass : Object.const_get(ns.join("::")))
store = options[:store].nil? ? PStore.new(name) : options[:store].new(name)
as_method = options[:as] || method
Memoize._set_store(name, store)
(class<<klass;self;end).instance_eval do # for Ruby1.9
method_name = "#{as_method}_without_memoize"
memoized_method_name = "#{as_method}_with_memoize"
define_method(memoized_method_name) do |*args|
store = Memoize._get_store(name)
ret = store.get(args)
if ret.nil?
ret = send(method_name, *args)
store.set(args, ret)
end
ret
end
alias_method method_name, method
alias_method as_method, memoized_method_name
end
end

# Specify unmemoize method.
# If you delete persistent cache when set delete_all true.
#
# == Example
# Memoize.memoize(self, 'slow_func')
# slow_func(arg)
# Memoize.unmemoize(self, 'slow_func')
#
def unmemoize(klass, name, delete_all=false)
(class<<klass;self;end).class_eval do
undef_method("#{name}_with_memoize")
alias_method name, "#{name}_without_memoize"
end
delete_all && @store.delete_all
end

# This method is public, but you don't need call directly.
def _set_store(name, store)
@@stores[name] = store
end

# This method is public, but you don't need call directly.
def _get_store(name)
@@stores[name]
end

# Not implemented
module Expire
end
end

if $0 == __FILE__
def fib(n)
return 1 if n < 2
fib(n-1) + fib(n-2)
end

class Math2
def self.fib(n)
return 1 if n < 2
fib(n-1) + fib(n-2)
end
end

class MemCacheStore < Memoize::Storable
attr_accessor :cache, :keys
def initialize(name)
@keys = []
@cache = MemCache.new 'localhost:11211', :namespace => 'rakuto.blogspot.com'
end

def get(key)
@cache.get(key)
end

def set(key, value)
@keys << key unless @keys.include?(key)
@cache.set(key, value)
end

def delete(key)
@cache.delete(key)
end

def delete_all
@keys.each { |key| @cache.delete(key) }
end
end


Memoize.register(self, 'Math2.fib', :store => MemCacheStore)
Benchmark.bm do |b|
b.report("fib(30) with memoize: ") { Math2.fib(30) }
#Memoize.unmemoize(self, 'fib')
b.report("fib(30)") { Math2.fib(30) }
end
end


考察
  • Memoizaionのテクニックは関数型言語と非常に相性がよさそう。
  • Railsのルーティング部分は副作用を含まないメソッド単位に分解して、Memoizedすることで高速化できないだろうか?

Wednesday, May 23, 2007

[Ruby] EXIFのGPS情報も取得できるExtExif 1.0をリリースした

extexif

RubyにはEXIF(Exchangeable image file format)を扱うための実装がいくつかあるが、全て開発がとまっている。Pure Rubyで実装されたruby-exifは、Ruby1.8だとパッチを当てないとコンパイルが通らない。二つ目はCで書かれたlibexifをRubyから呼び出せるように拡張ライブラリで実装されたruby-libexifであるが、2007年5月23日現在サーバが落ちている。幸いローカルにキャッシュがあったためコンパイルを試みたが、Ruby1.8.5ではコンパイルが通らない。Debainパッケージではコンパイルが通るように修正が施されているが、このライブラリはEXIFのGPSデータの取得をサポートしていない。

exif.c


#if 0
/* GPS tags are not supported in this library */
#endif


RubyのEXIF関連ライブラリはどれも開発がとまっている状況みたいなので、EXIFのGPS情報の取得もサポートするExtExifを作った。(RubyForge申請中なのでもう少し時間がかかりそう。)

インストール

% sudo gem install extexif


ドキュメント
ruby-libexifに実装されているExifクラスはそのまま利用可能であるが、もう一段抽象的な操作を提供するクラスとしてExtExifを実装している。現在のところは機能は少ないがこちらにEXIF関連で便利なメソッドとかを実装していく。

画像のパスを与えてクラスを生成して配列のようにEXIF Tag IDを使用してアクセスするだけです。

require 'rubygems'
require 'extexif'

# print EXIF data
IMG_FILE = 'image_with_exif.jpg'
image = ExtExif.new(IMG_FILE)
puts IMG_FILE
print "%-15s: %s\n" % ["Make", image["Make"]]
print "%-15s: %s\n" % ["GPSLatitude", image["GPSLatitude"]]
print "%-15s: %s\n" % ["GPSLongitude", image["GPSLongitude"]]



% ruby test_extexif.rb
image_with_exif.jpg
Make : KDDI-CA
GPSLatitude : 35.00, 39.00, 45.38
GPSLongitude : 139.00, 41.00, 16.89
%


EXIF Tag IDについては以下のサイトを参考に。
ExifTAG

exif.cに対するパッチのみいる人は以下。

< #include <ruby.h>
< #include <libexif/exif-tag.h>
---
> #include "ruby.h"
> #include <libexif/exif-ifd.h>
15a14
> #include <libexif/exif-tag.h>
137d135
< char value[1024];
142c140
< v = rb_str_new2(exif_entry_get_value(entry, value, sizeof(value)));
---
> v = rb_str_new2(exif_entry_get_value(entry));
219d216
< char value[1024];
246c243
< return rb_str_new2(exif_entry_get_value(e, value, sizeof(value)));
---
> return rb_str_new2(exif_entry_get_value(e));
253c250
< found = exif_entry_get_value(e, value, sizeof(value));
---
> found = exif_entry_get_value(e);
356c353
< rb_funcall(dest, rb_intern("<<"), 1, rb_str_new((char *)exif->ed->data, exif->ed->size));
---
> rb_funcall(dest, rb_intern("<<"), 1, rb_str_new(exif->ed->data, exif->ed->size));
379d375
< #define GPS_SUPPORT
457,488c453,454
< #ifdef GPS_SUPPORT
< {EXIF_TAG_GPS_VERSION_ID, "GPSVersionID", N_("GPS tag version")},
< {EXIF_TAG_GPS_LATITUDE_REF, "GPSLatutideRef", N_("North or South latitude")},
< {EXIF_TAG_GPS_LATITUDE, "GPSLatitude", N_("Latitude")},
< {EXIF_TAG_GPS_LONGITUDE_REF, "GPSLongitudeRef", N_("East or West longitude")},
< {EXIF_TAG_GPS_LONGITUDE, "GPSLongitude", N_("Longitude")},
< {EXIF_TAG_GPS_ALTITUDE_REF, "GPSAltitudeRef", N_("Altitude reference")},
< {EXIF_TAG_GPS_ALTITUDE , "GPSAltitude", N_("Altitude")},
< {EXIF_TAG_GPS_TIME_STAMP, "GPSTimeStamp", N_("GPS time (atomic clock)")},
< {EXIF_TAG_GPS_SATELLITES, "GPSSatellites", N_("GPS satellites used for measurement")},
< {EXIF_TAG_GPS_STATUS, "GPSStatus", N_(" GPS receiver status")},
< {EXIF_TAG_GPS_MEASURE_MODE, "GPSMessureMode", N_("GPS measurement mode")},
< {EXIF_TAG_GPS_DOP, "GPSDOP", N_("Measurement precision")},
< {EXIF_TAG_GPS_SPEED_REF, "GPSSpeedRef", N_("Speed unit")},
< {EXIF_TAG_GPS_SPEED, "GPSSpeed", N_("Speed of GPS receiver")},
< {EXIF_TAG_GPS_TRACK_REF, "GPSTrackRef", N_("Reference for direction of movement")},
< {EXIF_TAG_GPS_TRACK, "GPSTrack", N_("Direction of movement")},
< {EXIF_TAG_GPS_IMG_DIRECTION_REF, "GPSImgDirectionRef", N_("Reference for direction of image")},
< {EXIF_TAG_GPS_IMG_DIRECTION, "GPSImgDirection", N_("Direction of image")},
< {EXIF_TAG_GPS_MAP_DATUM, "GPSMapDatum", N_("Geodetic survey data used")},
< {EXIF_TAG_GPS_DEST_LATITUDE_REF, "GPSDestLatitudeRef", N_("Reference for latitude of destination")},
< {EXIF_TAG_GPS_DEST_LATITUDE, "GPSDestLatitude", N_("Latitude of destination")},
< {EXIF_TAG_GPS_DEST_LONGITUDE_REF, "GPSDestLongitudeRef", N_("Reference for longitude of destination")},
< {EXIF_TAG_GPS_DEST_LONGITUDE, "GPSDestLongitude", N_("Longitude of destination")},
< {EXIF_TAG_GPS_DEST_BEARING_REF, "GPSDestBearingRef", N_("Reference for bearing of destination")},
< {EXIF_TAG_GPS_DEST_BEARING, "GPSDestBearing", N_("Bearing of destination")},
< {EXIF_TAG_GPS_DEST_DISTANCE_REF, "GPSDestDistanceRef", N_("Reference for distance to destination")},
< {EXIF_TAG_GPS_DEST_DISTANCE, "GPSDestDistance", N_("Distance to destination")},
< {EXIF_TAG_GPS_PROCESSING_METHOD, "GPSProcessingMethod", N_("Name of GPS processing method")},
< {EXIF_TAG_GPS_AREA_INFORMATION, "GPSAreaInformation", N_("Name of GPS area")},
< {EXIF_TAG_GPS_DATE_STAMP, "GPSDateStamp", N_("GPS date")},
< {EXIF_TAG_GPS_DIFFERENTIAL, "GPSDifferential", N_("GPS differential correction")},
---
> #if 0
> /* GPS tags are not supported in this library */
490d455
<
567c532
< {-1, NULL, NULL}
---
> {0, NULL, NULL}
592c557
< for (i = 0; ExifTagTable[i].tag != -1; i++) {
---
> for (i = 0; ExifTagTable[i].tag != 0; i++) {


gemパッケージの中の写真はGOLDRU$Hの金曜限定ランチ1ポンドバーグ(450g)だというのは秘密。