緑川ゆきさんサイン会 (2 回目)2008年04月02日 01時34分

先日東京は八王子で開かれた緑川ゆきさんのサイン会に行ってきました。予約開始日の夕方になってそのことに気づき、もうだめだろうけど一応確認だけと思って電話を掛けてみたらなぜか予約が取れてしまったので、何とか当日までにやることを片付けていそいそと出発。

整理券兼アンケート用紙を受け取ってファーストフード店で昼食がてら記入し、書店に戻ると会場の地下 1 階から階段に沿って 6 階まで続く長い列。前回と同様 1 時間半近く並んでのご対面となりました。会場には丸提灯が吊り下げてあったりしたようですが緊張のためまったく目に入らず。言葉を交わした内容もよく覚えていませんが、握手もしていただき大満足のサイン会でした。

[冬絵・展 vol. 4 のチケット] [たこ焼き屋「HACHI-HACHI」] [熊本市電辛島町停留所] [熊本市電国府停留所]

写真の前半 2 枚は東京へ行ったついでに足を伸ばした冬目景原画展「冬絵・展 vol. 4」のチケットと、帰りに寄った富山県高岡市にあるたこ焼き屋「HACHI-HACHI」さんです。アニメ「true tears」に出てくる今川焼き屋「あいちゃん」のモデルとなったお店ですね。true tears やら何やらを見てからだと、昔はテーマが受け入れられなかった「蛍火の杜へ」収録の「ひび、深く」も切なさが胸に残ります。

後半 2 枚は後日九州を旅した際に通った熊本市電の辛島町停留所と国府停留所です。ここから名が採られた二人が活躍する「あかく咲く声」も 5 月に文庫化とのこと、発売日が待ち遠しいです。

そして待ち遠しいといえば「夏目友人帳」のアニメ化。雰囲気が似てるといわれることもある「蟲師」、スタッフの一部がかぶる「灰羽連盟」はともにすばらしいアニメでしたので、それらに匹敵するような作品となることを祈っています。(特に蟲師第 1 話「緑の座」、第 2 話「瞼の光」は、緻密な蟲の描写もさることながら、少年と少女という構図が私の好みにぴったりでした。「緑の座」の彼女を少女というのはちょっと違っているかもしれませんが。)

第 11 回 FLOSS 桜山勉強会発表資料2008年04月02日 01時37分

先日名古屋で開かれた FLOSS 桜山第 11 回勉強会で、JavaScript 1.7 で導入されたジェネレータに関する話をしてきました。発表資料などは以下の通りです。

補足 (質疑応答で出た内容も含む)

ジェネレータ、配列内包、分割代入は JavaScript 1.7 から、関数の省略表記、ジェネレータ内包、正規表現の先頭固定 (sticky) オプションは JavaScript 1.8 からの対応です。Firefox 2 は JavaScript 1.7 に、Firefox 3 は JavaScript 1.8 に対応していますが、いくつかの機能を使うためには <script type="application/javascript; version=1.7"> のように明示的にバージョン指定をしなくてはいけません。

「Haskell のリストっぽいもの」でやってることは、「無限リストと遅延評価」の「ジェネレータを使った無限リスト」および「JavaScript でカリー化、再び」の続きです。curry 関数を関数オブジェクトのメソッドとし、以下のような cons 関数を使っています。

const cons = function (head, tail) {
  yield head;
  for (var i in tail)
    yield i;
}.curry();

print 関数は、複数の引数を与えるとそれらをスペース区切りで出力する関数です。配列内包との組み合わせは以下のように解釈されます。

var list = cons(1, cons(2, cons(3, nil())));

print.apply(null, [i for (i in list)]);
// ↓
print.apply(null, [1, 2, 3]);
// ↓
print(1, 2, 3)
// => 1 2 3

JavaScript のジェネレータ機能は基本的に Python のそれと同じですが、ジェネレータイテレータオブジェクトの close メソッドの動作が異なります。close メソッドが呼び出されたとき、Python ではジェネレータの実行を再開し、再開地点で GeneratorExit 例外を投げますが、JavaScript ではジェネレータの実行を再開し、再開地点で return 文に出会ったかのようにジェネレータの実行を終了します。また、Pyothon では GeneratorExit、StopIteration 例外が close メソッドの呼び出し元に伝播しませんが、JavaScript ではジェネレータ内で投げられたすべての例外が close メソッドの呼び出し元に伝播します。

# Python 2.5
def gen():
  try:
    yield
  except:
    print "in except/catch clause"
  finally:
    print "in finally clause"
    raise StopIteration

g = gen()
g.next()
g.close()
# => in except/catch clause
# => in finally clause
// JavaScript 1.7
function gen() {
  try {
    yield;
  } catch (ex) {
    print("in except/catch clause");
  } finally {
    print("in finally clause");
    throw StopIteration;
  }
}
var g = gen();
g.next();
g.close();
// => in finally clause
// => uncaught exception: [object StopIteration]

配列内包およびジェネレータ内包の式部分には、関数呼び出しやオブジェクトの生成などを含む任意の式を掛けます。内包表記自体も式なので、入れ子にして使うこともできます。

[[j for (j in enumFromTo(1, i))]
  for (i in enumFromTo(1, 4))].toSource();
// => [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]
// enumFromTo 関数については後述

配列内包もジェネレータ内包も、for 節を重ねたり if 節を付加したりできます。

print.apply(null,
  [x.toSource()
    for (x in ([i, j] for (i in enumFromTo(1, 4))
                      for (j in enumFromTo(1, 4))
                      if (i < j)))]);
// => [1, 2] [1, 3] [1, 4] [2, 3] [2, 4] [3, 4]

正規表現の先頭固定オプションは、字句解析のときなどに使えるんじゃないかと思っています。ほかの言語を使ったりほかの手法を使ったりした場合に比べて、速度的にどうこうというのは調べていません。

現時点での JavaScript 1.7/1.8 の使いどころは、Firefox の拡張機能内部などに限られてくると思います。Web 上で気軽に使えるようになるのは果たしていつのことやら。

おまけ (関数合成)

本題からずれているような気がしたのと時間の都合で発表からははずしましたが、Haskell の . 演算子のようなことをする compose 関数も作ってみました。

const compose = (function (f, g)
  (function () f(g.apply(null, arguments))).lengthIs(g.length).curry()
).curry();

lengthIs メソッドがやっていることは「JavaScript でカリー化、再び」の setParameterLength 関数と同じです。引数 ij が与えられると、i から j までの値を収めたリストを返す enumFromTo 関数なども定義して、Haskell での階乗を求める関数の書き方を模倣してみます。

-- Haskell
fact = product . enumFromTo 1
const enumFromTo = function (from, to) {
  if (from > to) return;
  yield from;
  for (var i in enumFromTo(from + 1, to))
    yield i;
}.curry();

const foldl = function (folder, initial, list) {
  try {
    return foldl(folder, folder(initial, list.next()), list);
  } catch (ex if ex instanceof StopIteration) {
    return initial;
  }
}.curry();

const mul = (function (a, b) a * b).curry();

const product = foldl(mul, 1);

const fact = compose(product, enumFromTo(1));

print.apply(null, [fact(i) for (i in enumFromTo(1, 5))]);
// => 1 2 6 24 120

別の書き方も試してみます。

-- Haskell
fact n = (foldl (.) id [\x -> x * k | k <- [1..n]]) 1
const id = (function (x) x).curry();

const fact = function (n)
  foldl(compose, id, (mul(i) for (i in enumFromTo(1, n))))(1);

print.apply(null, [fact(i) for (i in enumFromTo(1, 5))]);
// => 1 2 6 24 120

ちなみにこの enumFromTo 関数で無限リストを作ることもできます。

print.apply(null,
  [i for (i in take(10, enumFromTo(1, Infinity)))]);
// => 1 2 3 4 5 6 7 8 9 10

WEB+DB PRESS 連載2008年04月25日 00時24分

現在発売中の WEB+DB PRESS Vol. 44 より、「JavaScript + ブラウザ探検」という連載を始めることになりました。初回は「JavaScript + ブラウザ温故知新」として、DOM の基本概念や JavaScript を扱えるブラウザの歴史などをまとめています。次回以降は DOM や HTML 5 といった、ブラウザで利用できる API を中心に話を進めていこうと思っています。

というわけでタイトルに「JavaScript」と入ってる割に、純粋な JavaScript の話はあまりなさそうですが、ブラウザ上で何かを実現するためのヒントとなれれば幸いと思っていますので、よろしくお願いします。