JavaScript の変数と delete 演算子2008年01月09日 07時13分

Kanasan.JS JavaScript 第 5 版読書会 #1 にて delete 演算子の動作が話題に上ったそうです。そこで、それについてちょっとまとめてみようかと思い立ったはいいものの、ずるずると引き伸ばしているうちに年を越してしました。しかし、読書会 #2 の開催も決まり、もうこれ以上引き伸ばしているといつまでたっても書けなさそうなので、いい加減腹をくくって個人的にまとめてみようと思います。

JavaScript の変数

delete 演算子の話に移る前に、変数とは何なのかおさらいしておきましょう。JavaScript において、変数とはプロパティの別名です。といっても、すべてのプロパティを変数というわけではありません。一般には、グローバルオブジェクトと Activation オブジェクトのプロパティを指して変数といい、特に前者をグローバル変数、後者をローカル変数と呼びます。

Activation オブジェクトとは、関数の引数やローカル変数をプロパティとして持つオブジェクトです。Call オブジェクトと呼ばれることもあり、関数を実行するたびに作られます。ある関数が引数 arg とローカル変数 local を持つということは、その関数が呼び出される際に作られる Activation オブジェクトが arg プロパティと local プロパティを持つということに他なりません。関数の再帰呼び出しをする際に、呼び出し元と呼び出し先とで同名のローカル変数の指す値が独立していられるのは、それぞれの呼び出しに対して Activation オブジェクトが別個に作られるからです。

グローバルオブジェクトはグローバルコードにおける this キーワード、または (ブラウザ上で実行しているなら) グローバル変数 window などから参照できますが、Activation オブジェクトを JavaScript のコードから直接参照することはできません。ここで、グローバル変数 window といいましたが、これはもちろんグローバルオブジェクトの window プロパティのことです。ブラウザ上では Window オブジェクトがグローバルオブジェクトとなり、また Window オブジェクトは自分自身を指す window プロパティを持っているからです。

var global = 42;
this.global; // => 42
// グローバルコード (関数の内側ではない
// トップレベルのコード) では、this は
// グローバルオブジェクトを指す。

this.global2 = 12;
global2; // => 12
// グローバルオブジェクトのプロパティはグローバル変数となる。

function f() {
  var local = 36;
  // Activation オブジェクトは JavaScript からは
  // 参照できない。すなわち、x.local がローカル
  // 変数 local と等しくなるような x は存在しない。
}
f();

delete 演算子の対象

さて、それではいよいよ本題、delete 演算子が何をしているかですが、端的にいえば delete 演算子のしていることとはプロパティの削除です。delete 演算子はあるオブジェクトを対象とし、そのオブジェクトのプロパティを削除します。ですが、ここで delete 演算子の対象となるのがどのオブジェクトなのかということには十分注意しなくてはいけません。具体的な例を C++ の delete 演算子とも比べながら見ていきましょう。

// C++
class Object {
public:
  Object *x;
};

int main() {
  Object o;
  o.x = new Object();
  delete o.x;
  return 0;
}
// JavaScript
var o = {};
o.x = new Object();
delete o.x;

どちらもオブジェクト o のプロパティ (メンバ変数) x に新しいオブジェクトを代入した後、それに対して delete 演算子を適用しています。この C++ のコードのうち、delete 演算子の部分は以下のように書き換えられます。

// delete o.x (C++)
o.x->~Object();
::operator delete(o.x);

o.x が指すオブジェクトのデストラクタを呼び出した後、o.x を引数として operator delete 関数を呼び出しています。つまり、この C++ のコードにおいて、delete 演算子の対象となるオブジェクトは o.x が指すオブジェクトであるといえるでしょう。

一方、JavaScript では delete 演算子を使わずに書き換えることはできませんが、概念的には以下のような操作を行っています。

// delete o.x (JavaScript)
o.[[Delete]]("x");

オブジェクト o の [[Delete]] 内部メソッドを、文字列 "x" を引数として呼び出しています。内部メソッドとは、実際の JavaScript コードからは見えない内部的な操作の説明のために導入されたメソッドで、[[Delete]] 内部メソッドは引数として渡された名前のプロパティを削除します。すなわち、JavaScript では delete 演算子の対象となるオブジェクトは o が指すオブジェクトなのです。delete 演算子の実行による変化はオブジェクト o からプロパティ x がなくなるということだけで、o.x が指していたオブジェクトには基本的に何の変化ももたらしません。

もちろん、実際の JavaScript 処理系のほとんどは、プロパティ x が削除された結果、o.x が指していたオブジェクトがどこからも参照されなくなったのなら、そのオブジェクト自体を削除し、メモリ領域を解放するといった操作を行っています。しかし、このことは ECMAScript の仕様として決められたことではなく、極端な話メモリ領域を使い捨てにして、一度作ったオブジェクトは決して削除しない実装があったとしても仕様に違反してはいないはずです。このことからも、JavaScript の delete 演算子が、メモリ領域の解放を目的とした C++ の delete 演算子とはまったくの別物であることがわかるでしょう。

なお、ここではオブジェクト o の x プロパティを指定するのに delete o.x としましたが、これは delete o["x"] と書いても同じことです。

変数に対する delete 演算子

上の内容を言い直すと、JavaScript の delete 演算子とは、あるプロパティが与えられたとき、そのプロパティを持つオブジェクトから、そのプロパティを削除するものだということです。ここで、変数とはプロパティの別名であるといったことを思い出してください。変数に対する delete 演算子の動作も同じように説明できます。以下では、便宜上グローバルオブジェクトを [[Global]] で、実行中の関数の Activation オブジェクトを [[Activation]] で参照できるものとします。

var global1 = 42;
delete global1;
// -> [[Global]].[[Delete]]("global1");

var global2 = 12;

function f() {
  var local = 36;
  delete local;
  // -> [[Activation]].[[Delete]]("local");

  delete global2;
  // -> [[Global]].[[Delete]]("global2");
  // Activation オブジェクトは global2 プロパティを
  // 持っていないので、スコープチェーンをたどって
  // グローバルオブジェクトからプロパティを探す。
  // グローバルオブジェクトでは無事プロパティが
  // 見つかるので、グローバルオブジェクトの
  // [[Delete]] 内部メソッドが呼び出される。
}
f();

削除できるプロパティとできないプロパティ

delete 演算子を使えばどんなプロパティも削除できるというわけではありません。まず、対象となるオブジェクト自身が持つプロパティでないと削除できません。プロパティの探索自体はプロトタイプチェーンをたどって行われますが、そこで見つかったプロパティは削除されないということです。

function C() { this.x = 42; }
C.prototype.x = 12;

var o = new C();
o.x; // => 42

delete o.x;
o.x; // => 12
// o 自身の x プロパティが削除されたので、
// o のプロトタイプチェーンをたどって
// C.prototype が持つ x プロパティの値が返される。

delete o.x;
o.x; // => 12
// o 自身はもはや x プロパティを持っていないので、
// プロパティの削除は行われない。
// プロトタイプチェーン上のオブジェクトが持つ
// x プロパティは削除されない。

また、プロパティは ReadOnly や DontDelete といった属性を持つことがあります。DontDelete 属性を持つプロパティは削除できません。もともと存在しないプロパティを代入によって新たに作った場合 DontDelete 属性は付きませんが、組み込みオブジェクトに最初から存在するプロパティの中には DontDelete 属性を持つものもあります。

var re = /abc/i;

delete re.ignoreCase;
re.ignoreCase; // => true
// RegExp オブジェクトの ignoreCase プロパティは
// DontDelete 属性を持つので削除できない。

re.newProperty = 42;
delete re.newProperty;
re.newProperty; // => undefined
"newProperty" in re; // => false
// 代入により新しく作られたプロパティは
// DontDelete 属性を持たないので削除できる。

変数の属性

繰り返しますが、変数とは特定のオブジェクトのプロパティです。var 文により変数を宣言するということは、特定のオブジェクトにプロパティを追加するということです。この特定のオブジェクトのことを変数オブジェクトと呼び、グローバルコードならグローバルオブジェクト、関数内ならその関数が実行される際に作られる Activation オブジェクトがそれに該当します。

var 文により作られるプロパティは DontDelete 属性を持ちます。つまり、var 文によって宣言された変数は削除できないということです。これは関数宣言によって作成されたプロパティも同じです。

var x = 42;
delete x;
x; // => 42
// var 文により作られた x プロパティは
// DontDelete 属性を持つので削除できない。

y = 12;
delete y;
y; // => ReferenceError: y is not defined
// var 文を使わずに作られたyプロパティは
// DontDelete 属性を持たないので削除できる。

function f() { return 36; }
delete f;
f(); // => 36
// 関数宣言により作られた f プロパティは
// DontDelete 属性を持つので削除できない。

f = 24;
f; // => 24
// f プロパティが指す値を変更することはできる。

しかし、これにはひとつ例外があります。それが eval 関数を使って実行されるコード、eval コード内でのことです。eval コード内での変数オブジェクトは eval 関数を呼び出した時点のものと同じになります。たとえば、グローバルコードで eval 関数を呼び出し、その eval コード内に var 文が含まれていたとき、その var 文により作られるプロパティはグローバルオブジェクトのものとなります。ですが、そのプロパティは DontDelete 属性を持ちません。eval コード中の関数宣言についても同様です。

eval("var x = 42;");
x; // => 42
delete x;
x; // => ReferenceError: x is not defined
// eval コード内の var 文により作られたプロパティは
// DontDelete 属性を持たないので削除できる。

eval("function f() { return 12; }");
f(); // => 12
delete f;
f(); // => ReferenceError: f is not defined
// eval コード内の関数宣言により作られたプロパティは
// DontDelete 属性を持たないので削除できる。

ただし、eval コード中に含まれる関数内では、var 文により作られるプロパティが DontDelete 属性を持ちます。

eval("(function () {" +
     "  var x = 42;" +
     "  delete x;" +
     "  return x;" +
     "})();")
// => 42
// eval コード中に含まれる関数内で var 文により作られた
// プロパティは DontDelete 属性を持つので削除できない。

このように eval コード中で var 文および関数宣言の扱いが変わってくることは、ECMA-262 3rd 10.1.3 変数の実体化および 10.2.2 eval コードにて定められています。なお、ECMAScript 4 ではこの動作が修正され、eval コード中の var 文により作られたプロパティも DontDelete 属性を持つようになる予定です。

delete 演算子の返り値

delete 演算子は単項演算子であり、typeof 演算子や ! 演算子といったほかの単項演算子と同様、返り値を持ち他の式に組み込んで使うこともできます。delete 演算子の返り値は真偽値であり、対象のオブジェクトが指定されたプロパティを持ち、かつそのプロパティが DontDelete 属性を持つときは false、その他の場合は true です。

function C() { this.x = 42; }
C.prototype.y = 12;
var o = new C();

delete o.x; // => true
o.x; // => undefined
"x" in o; // => false
// o の x プロパティは DontDelete
// 属性を持たないので true を返す。

delete o.y; // => true
o.y; // => 12
// o 自身は y プロパティを持たないので true を返す。

delete o.z; // => true
// o は z プロパティを持たないので true を返す。

delete o; // => false
// グローバルオブジェクトの o プロパティは
// DontDelete 属性を持つので false を返す。

delete undefinedProperty; // => true
// undefinedProperty プロパティをもつ
// オブジェクトは存在しないので true を返す。

本来なら delete 演算子をプロパティでない値に適用したときも true が返るはずですが、このような場合に例外を投げる実装もあるようです。

delete 42; // => true
// 42 はプロパティでないので true を返す。
// 例外を投げる実装 (ECMAScript 仕様には違反) もある。

var x = 24;
delete x; // => false
delete x++; // => true
x; // => 25
// 式 x++ を評価した値は 24 という数値であり、
// グローバルオブジェクトの x プロパティ
// ではないので、delete x++ は true を返す。

ちなみに、ここではプロパティかどうかといっていますが、ECMAScript 仕様では Reference 型という内部的なデータ型を説明のために導入し、delete 演算子の被演算子を評価した結果の値が Reference 型の値でなければ true を返すとしています。

Kanasan.JS JavaScript 第 5 版読書会 #22008年01月21日 19時49分

Kanasan.JSJavaScript 第 5 版読書会 #2 へ行ってきました。今回は午前ライトニングトーク、午後読書会という二部構成。他の参加者の感想等は参加者のブログ記事一覧からどうぞ。参加人数が 50 人近くという大規模な読書会を企画し、無事成功に導いてくださった Kanasan さんはじめスタッフの方々、そして参加者の皆さん、本当にありがとうございました。

ライトニングトーク

Lightning Talk 一覧および Lightning Talk 発表資料一覧から各 LT に関する情報が見られます。

Mozilla Developer Center 翻訳事始

私も LT に参加したのですが、JavaScript に関することなら何でも OK とあるのを見落としていてほとんど関係ない話 (一応翻訳作業の実演は Core JavaScript 1.5 リファレンスの arugments の項を例にとって行いましたが)、しかも 5 分と申告しておきながら倍以上の時間を使ってしまいました。

内容は OmegaT を使った Mozilla Developer Center の翻訳作業の進め方ですが、これはあくまで個人で使っているだけであって、翻訳作業に OmegaT を使うと決まっているわけではありません。

JSDeferred による非同期処理

東京から駆けつけてくださった amachang さんが、cho45 さん作成の JSDeferred を紹介。JSDeferred は Python で書かれたネットワークアプリケーション用フレームワーク Twisted に影響を受けているそう (正確には JSDeferred は MochiKit Deferred に影響を受けていてMochiKit Deferred が Twisted に由来している?)。エラーハンドリングもできる。しかしついつい deffer と typo してしまいます。

単に自分の開発経験を (話せる範囲で) 話そう

AWAWA さんによる自身の JavaScript 歴の話。プロトタイプの存在を知らずに以下のように書いていたというのは私も同じです。

function A() {
  this.method = function () {};
}
var a = new A();

Script.aculo.us の UnitTest の使い方

ema さんによるプレゼン。script.aculo.us はスクリプトアークラスと読む?

Javascript で無限ループを実現する 5 つの方法

yhara さんによる非同期処理を同期処理のように書く話。最後の方法は自分で言語処理系を作ってしまうこと。YARV はヤーブと読む?

JavaScript から見た Haskell

氏久さんによる Haskell の話。Ruby は自然なコード、Haskell はシンプルなコードが書けることを目標にしている。Haskell は草案を書くのにお勧めとのこと。

Flex & de.popforge.audio ライブラリを使ってリアルタイムで音生成と再生

井上謙次 (deq) さんによる音声合成の実演。自然な音声を出すためには少なくとも 6000Hz (秒間 6000 回)、つまり 60fps (秒間 60 回) の動画と比べて 100 倍の頻度で処理をしなくてはいけないみたい。

読書会

50 人近くが机を四角に並べて向き合うという異例の読書会。「6 章 文」から「7.4 Object のプロパティとメソッド」まで読み進めました。以下読書会中に話題になった事柄を補足も交えて取り上げていきます。

式と文

if (x) { ... } としたときに、x に書けるものが式、書けないものが文。

文にも返り値が存在するが、普通のコード上からは見えない。文の返り値は JavaScript で通常扱える値以外に empty という値を取りうる。eval 関数を使ってプログラムを評価すると、そのプログラム中で最後に empty でない値を返した文の返り値を、eval 関数の返り値として見ることができる。

eval("1 + 2"); // => 3
// セミコロンの自動補完により式文 1 + 2; と解析される。
// 式文の返り値は式分を構成する式の返り値なので、
// 式 1 + 2 の返り値である 3 が eval 関数の返り値となる。

eval("42; var x = 12;"); // => 42
// var 文の返り値は empty なので、式文 42; の
// 返り値である 42 が eval 関数の返り値となる。

eval("var x = 42;"); // => undefined
// すべての文の返り値が empty である場合、
// eval 関数の返り値は undefined となる。

function キーワードを用いた構文

function キーワードが関数宣言の一部として使われているのか、関数式の一部として使われているのかは、その function キーワードが出現した時点で決まる (文が終了した直後、すなわち新たな文の開始位置に function キーワードが来れば、それは関数宣言のものとみなされる)。関数が名前つきか無名か、関数本体の後に何が続くかなどは関係ない。

eval("function f() {}"); // => undefined
// 関数宣言の文としての返り値は empty。

eval("function add(a, b) { return a + b; }(2, 3);");
// => 3
// function add... が関数宣言として扱われるので、
// (2, 3) の括弧は関数呼び出しの括弧ではなく
// 式をまとめるための括弧、カンマは引数リストの
// 区切りではなくカンマ演算子として扱われる。
// 式文 (2, 3); の返り値 3 が eval 関数の返り値となる。

eval("+function add(a, b) { return a + b; }(2, 3);");
// => 5
// 単項 + 演算子の後に文が来ることはできない
// (+if (...) とは書けない) ので、+ に続く
// function は名前付き関数式の始まりとみなされる。
// 関数式の関数本体の後に続く括弧は
// 関数呼び出しの括弧とみなされ、
// 関数呼び出しの結果 5 に単項 + 演算子を
// 適用した結果 5 が eval 関数の返り値となる。

eval("function () {}");
// ECMAScript 3 => 構文エラー
// ECMAScript では関数宣言には名前が必要。
//
// SpiderMonkey 1.8 => function () {
//                  => }
// SpiderMonkey (Firefox などで使われている
// JavaScript エンジン。バージョン 1.8 は現在
// 開発途中) のバージョン 1.6 以降では無名の
// 関数宣言が可能。この場合、文としての
// 返り値は Function オブジェクトとなる。 
//
// JScript 5.6 => undefined
// JScript でもエラーにはならないが、
// 文としての返り値は empty みたい。

eval("function (a, b) { return a + b; }(2, 3);");
// ECMAScript 3 => 構文エラー
//
// SpiderMonkey 1.8 => 3
// JScript 5.6 => 3
// 後に何が続こうと、関数宣言と解釈されたものが
// 関数式として解釈されなおすことはない。

eval("+function (a, b) { return a + b; }(2, 3);");
// => 5
// 関数式は無名でもよい。

eval("(function f() {});");
// => function f() {
// => }
// 開き括弧の後に文が来ることはできないので
// 関数式とみなされる。関数式の返り値は
// Function オブジェクトなので、式文
// (function f() {}); および eval 関数の
// 返り値もその Function オブジェクトになる。
//
// JScript 5.6 => undefined
// 謎の挙動。

ECMAScript では関数宣言はトップレベルまたは関数の直下にしか置けない (if 文内などには置けない) が、JavaScript や JScript ではブロック下の関数宣言も構文エラーにならない。このあたりは Function Expression Statements も参考に。

Primary Expressions

「文の直後の function f() {}(x) の最後の括弧は primary expression の括弧」と発言したことから primary expressions (基本式) って何という話題に。JavaScript の文法定義中に出てくる用語で、this キーワード、識別子、プリミティブ値のリテラル、配列リテラル、オブジェクトリテラル、括弧でくくった式の集合。

for-in 文

91 ページ、オブジェクトのプロパティ名を配列に抜き出すコードがすごい。応用すれば JavaScript 1.7 以上では 1 回の反復でプロパティの名前と値を抜き出せそう。

var o = { a: 10, b: 20, c: 30 };
var keys = [], values = [];
for ([keys[keys.length], values[values.length]] in new Iterator(o))
  ;
keys; // => a,b,c
values; // => 10,20,30

break 文

continue 文につけるラベルはループ文に対するものでなくてはいけないが、break 文につけるラベルはどんな文に対するものでもいい

eval 関数内での break 文

eval 関数を使って break 文の行き先を動的に変えることはできない。

outer: {
  inner: {
    var destination = "outer";
    eval("break " + destination);
    // => SyntaxError: label not found:
  }
}

これは eval 関数に渡された文字列がひとつのプログラムとして解析されるから。プログラムのトップレベルにラベル付き break 文は置けない。

空文

私は空文に一行とる派。

while (...)
  ;

var 文はなぜ文か

var は式でもいいのではという amachang さんの提起。実際 Perl の my は関数。var は互換性の観点から仕方ないとして、JavaScript 1.7 で let 宣言が導入されたときは私も同じことを言おうと思ったが、if (let x = foo()) { ... } else { ... } としたときに x のスコープはどうなるのかと考えたら面倒くさくなってやめてしまった。

ドットは演算子か

プロパティアクセスに使われる . は演算子かという話。79 ページにも演算子として扱うと書いてあるし、演算子の優先順位や結合規則を統一的に扱う上でも演算子として扱ったほうが説明が簡単になると個人的には思う。それから演算子であることとシンタックスシュガーであることは両立しうる気が。

. と [] はどちらが速いか。

o.xo["x"] で速度に差はあるのかという話。SpiderMonkey の場合、[] 内が文字列リテラルなら (そもそも [] 内が文字列リテラルでない場合、ドットによる書き換えはできない) 両者とも同じ中間コードになるので、実行時の速度を測っても差は出ないはず。ドットのほうが観測できないレベルでコンパイルが速いかもといった感じか。

配列リテラル中の空要素

配列リテラル中に空要素があった場合、プロパティ自体が作られない。

var a = [undefined, undefined,];
a.length;
// => 2
// JScript 5.6 => 3
// JScript では最後のカンマが無視されない。
a.hasOwnProperty("0");
// => true
// JavaScript ではプロパティ名は
// すべて文字列として扱われる。
// JScript 5.6 => false
// JScript では明示的に undefined を
// 指定していてもプロパティが作られない。

var b = [, ,];
b.length;
// => 2
// JScript 5.6 => 3
b.hasOwnProperty("0");
// => false
// SpiderMonkey 1.8 => true
// SpiderMonkey では空要素に対しても
// undefined を値とするプロパティを
// 作ってしまう (Mozilla Bug 260106)。

constructor プロパティ

自作オブジェクトの constructor プロパティの値は何になるかという話。まず前提として、Function オブジェクト A が作られたとき、同時に新しくオブジェクトが作られ、A の prototype プロパティに設定される。そしてそのオブジェクトは、基の Function オブジェクト A を指す constructor プロパティを持つ。

function A() {}
A.hasOwnProperty("prototype"); // => true
A.prototype; // => [object Object]
A.prototype.hasOwnProperty("constructor"); // => true
A.prototype.constructor == A; // => true

A をコンストラクタとして作られたオブジェクト a 自身は constructor プロパティを持たない。しかし、a の [[Prototype]] 内部プロパティ (__proto__ プロパティ) が指すオブジェクト A.prototype は constructor プロパティを持つ。プロパティの探索はプロトタイプチェーンをたどって行われるので、a.constructor の値は A.prototype.constructor の値である A となる。

var a = new A();
a.hasOwnProperty("constructor"); // => false
a.__proto__ == A.prototype; // => true
a.constructor == A; // => true

ここで、新しくオブジェクトoを作る。オブジェクトリテラルによるオブジェクトの作成は、Function オブジェクト Object をコンストラクタとして呼び出したのと同じことなので、o の [[Prototype]] 内部プロパティの値は Object.prototype が指すオブジェクトになる。o 自身は constructor プロパティを持たないが、Object.prototype が指すオブジェクトは constructor プロパティを持ち、その値は Object である。よって、o.constructor の値は Object となる。

var o = {}; // == new Object()
o.__proto__ == Object.prototype; // => true
o.hasOwnProperty("constructor"); // => false
Object.prototype.constructor == Object; // => true
o.constructor == Object; // => true

今、o を A.prototype に設定した上で A をコンストラクタとして新しくオブジェクト b を作る。b も b の [[Prototype]] 内部プロパティの値である o も constructor プロパティを持たないので、b.constructor の値はさらにプロトタイプチェーンをさかのぼって Object.prototype の constructor プロパティの値となる。

A.prototype = o;
var b = new A();
b.hasOwnProperty("constructor"); // => false
b.__proto__ == o; // => true
b.constructor == Object; // => true

こういった事態を防ぐためには、A.prototype に constructor プロパティを設定すればよい。

A.prototype.constructor = A;
var c = new A();
c.hasOwnProperty("constructor"); // => false
c.constructor == A; // => true

懇親会

周りからプロトタイプがどうのスコープがこうの聞こえてくるのを尻目に、まったりと皿をつつくグループに属していました。終盤では amachang さんの隣にお邪魔してお話を拝聴。自分はブログに書く速度が遅いということを言いましたが、さすがに DOM 2 Events の JavaScript による実装における、最初のプロトタイプ作成から 1 年以上というのは例外的な部類に入ります。それでもブログに書こうと思ってから実際に記事として公開できるまで 1 週間くらいかかるのは普通といった感じですが (^^;

Google の大規模データ処理2008年01月25日 19時59分

Google の鵜飼文敏さんによる講演会「大規模データ処理を可能にする Google の技術」に行ってきました。内容的には筑波大学で開かれたものと同じではないかと思います (「新ビジネスモデル」がそのままだったことなどから)。以下、上記記事に載っていないことを中心にメモから抜書きを。

此頃 Google にはやる物
現在 Google では Google の使命 (Google's mission is to organize the world's information and make it universally accessible and useful...) の早打ちが流行中。鵜飼さんは 50 秒程度、一番速い人は 30 秒程度。
Google の扱う情報
Google のいう「情報」はインターネット上のものだけに限らない (例: Google ブック検索)。
データセンター構築の方針
初期のころは「いかに狭い部屋にマシンを詰め込むか」、現在は「いかに故障した部品を交換しやすくするか」。
電力コスト
ハードウェア性能は向上したが、性能あたりの電力消費量は改善していない。
4 年間の電力コストがハードウェアコストの半分を超える。
スケール = コスト
大規模なソフトウェアは大規模なハードウェアを要求し、大規模なハードウェアはお金 (コスト) を要求する。
SQL DB は使わない
データの分析は単純なもの (合計、最大値、最小値、上位 k 個、フィルタリングなど) がほとんどで、DBMS の高度な機能は必要ない。
これらの分析処理は可換的、結合的なため処理順は任意。
MapReduce
Google の開発した大規模データ処理のためのプログラミングモデル。自動的並列分散化、耐障害性、I/O 最適化に優れている。
日本語での解説は Radium Software Development に詳しい。講演中に使っていた図及び例もほとんどそのまま。
Map、Reduce 関数は C++ で記述。
Map から Reduce に渡す処理を「シャッフル」と呼んでいた。内容的には「ソート」のほうが適切な気がするのに、なぜ「シャッフル」というのか?
Sawzall
MapReduce 上で動くデータ分析用言語。強い静的型のスクリプト言語。
限定詞によるループ、if の簡略化が可能。when (i : each int; queries[i].language = JAPANESE) { ... } (foreach (queries 中の query) { if (query.language = JAPANESE) { ... } } より最適化しやすいのか?)
Google での利用に最適化されている (フィンガープリントや日付が組み込みのデータ型になっているなど)。
例外処理はなく、異常時には undefined を返す。undefined は無視して処理を進めることも可能。
Bigtable
数千台のサーバ、テラバイト規模のメモリ、ペタバイト規模のディスクを扱い、毎秒 100 万 Read/Write を可能にするデータベースシステム。
疎な分散多次元表。(行, 列, タイムスタンプ) でアクセス。
行に対する操作はアトミック。
連続する数行をタブレットという単位にまとめて管理。
GFS (Google File System)、Chubby、SSTable といった Google のシステムの上に構築。
日本語での解説は steps to phantasien に詳しい。
タブレット
SSTable (不変二次元マップ) とログデータ (SSTable からの更新分) の組み合わせ。
SSTable は書き換えできないので、追加、削除分はログデータに記録する。ログデータがたまってきたら SSTable を作り直す。
データ量がもたらすもの
計算能力が増えると難しい問題が解けるようになるというのはよく知られているが、データ量が増えると解決不能だった問題が解けるようになるというのはあまり知られていない。
データ量の増加が可能にしたアプリケーションの例
スペル訂正 (「もしかして」機能など)。Web を文脈つきの辞書とみなす。
Google が公開している論文など
以上の技術の概要については Papers Written by Googlers で参照できる。
東京オフィスの位置づけ
東京オフィスはローカライズオフィスではない。

質疑応答では「もしかして」機能に関する質問が多く出ていました。「答えられない」という回答もちらほら。

「もしかして」で間違った結果をどう直すか
間違った結果を目視で直したりはしない。すべて機械処理。
テキスト処理は言語非依存。
BM 法を検索したら KMP 法が引っかかった
ユーザによってはそれが正しい。究極的にはパーソナライゼーションで解決すべきこと。
1 日にコードを書く時間は
日によって違うが、1 日 5 時間くらいコードを書く。レビューなどでコードを読む時間も多い。
日本語の処理に使っている知識は
日本語の知識の利用は皆無。品詞情報なども使っていない。
C++ をどのような言語として使っているか
基本的にオブジェクト指向言語として使っているが、部署によっては better C として使ったりテンプレートを使ったりすることも。
例外は使わない。Google の規模だとほぼすべての実行パスを通るため、見た目がわかりやすくなっても処理を追いにくくなる。
Android に関して、Java 言語を使いながら、Java ME 上のライブラリとしてではなく、既存のものと互換性のないプラットフォームとして作り出す必然性は
開発責任者がそのほうがいいと思ったのだろう。

終了後、鵜飼さんとのじゃんけんに勝って Google T シャツをゲット。さらに残って話を聞いていたところ、余った Google T シャツももらったので、結局白黒 2 枚の T シャツ (前面に Google ロゴ、背面に「I'm Feeling Lucky!」)をいただくこととなりました。