2011年10月20日木曜日

日本語エンコーディングの可視化

世の中の文字コードはUnicodeに統一される方向で動いているようで、ウェブアプリを作っていた頃に文字コードに悩まされた者としては、慶ばしい限りです。が、諸事情でレガシーコード("Shift_JIS"とか)を扱う必要があり、このサイトで勉強し直しました。以下がエンコーディングのまとめです。なお、「符号化文字集合」については省略します。


ISO-2022-JP
メールに使われているエンコーディング。それ以外ではみたことない。JISコードと呼ばれるらしいけど、個人的にはあんまり聞いたことないです。
ISO-2022-JPはISO-2022準拠のエンコーディングなので、複数のテーブルをエスケープシーケンスによって切り替えて使います。(ISO-2011-JPの説明は複雑なのでここでは省略します。このページをみてください。)簡単に言うと、特定の制御文字列を送ることで、「これからアルファベットを使います」「これから漢字を使います」と、状態を切り替えることで、たった7bitでアルファベット、数字、漢字をすべて表現することができるのです。

エスケープシーケンス
  • これから送る文字はASCIIです: 0x0F, "ESC ( B", or "ESC ( J"
  • これから送る文字は漢字です: "ESC $ @", or "ESC $ B"
  • これから送る文字は補助漢字です: "ESC $ ( D"
  • これから送る文字は半角カタカナです: 0x0E, or "ESC ( I"
なお、初期状態はASCIIです。なので、アルファベットと数字だけからなるメールにはエスケープシーケンスはいっさい含まれていません。(未確認ですがそのはず)

使用領域
7bitの領域をどのように使っているのか図示します。なお、制御文字が水色、ASCIIがオレンジ、漢字・補助漢字が緑、半角カタカナがグレーです。



ASCII使用時:
01234567
00010203040506070
10111213141516171
20212223242526272
30313233343536373
40414243444546474
50515253545556575
60616263646566676
70717273747576777
80818283848586878
90919293949596979
a0a1a2a3a4a5a6a7a
b0b1b2b3b4b5b6b7b
c0c1c2c3c4c5c6c7c
d0d1d2d3d4d5d6d7d
e0e1e2e3e4e5e6e7e
f0f1f2f3f4f5f6f7f

漢字・補助漢字使用時:
01234567
00010203040506070
10111213141516171
20212223242526272
30313233343536373
40414243444546474
50515253545556575
60616263646566676
70717273747576777
80818283848586878
90919293949596979
a0a1a2a3a4a5a6a7a
b0b1b2b3b4b5b6b7b
c0c1c2c3c4c5c6c7c
d0d1d2d3d4d5d6d7d
e0e1e2e3e4e5e6e7e
f0f1f2f3f4f5f6f7f

半角カタカナ使用時:
01234567
00010203040506070
10111213141516171
20212223242526272
30313233343536373
40414243444546474
50515253545556575
60616263646566676
70717273747576777
80818283848586878
90919293949596979
a0a1a2a3a4a5a6a7a
b0b1b2b3b4b5b6b7b
c0c1c2c3c4c5c6c7c
d0d1d2d3d4d5d6d7d
e0e1e2e3e4e5e6e7e
f0f1f2f3f4f5f6f7f


Shift_JIS / Windows-31J など
依然としてクライアントサイドでは最も使われているエンコーディング。符号化文字集合が微妙に異なる親戚がたくさんいる。Shift_JISと呼ばれているものの多くは正確にはWindows-31Jであろう。
エスケープシーケンスを使わずASCII、漢字、半角カタカナを表現可能なのが便利だが、あるバイトを見たときに、それがASCIIなのか漢字を表現する2バイト目なのかを判別できない(場合がある)ので、波紋を呼んだりもした。

使用領域
1バイト目:
0123456789abcdef
000102030405060708090a0b0c0d0e0f0
101112131415161718191a1b1c1d1e1f1
202122232425262728292a2b2c2d2e2f2
303132333435363738393a3b3c3d3e3f3
404142434445464748494a4b4c4d4e4f4
505152535455565758595a5b5c5d5e5f5
606162636465666768696a6b6c6d6e6f6
707172737475767778797a7b7c7d7e7f7
808182838485868788898a8b8c8d8e8f8
a09192939495969798999a9b9c9d9e9f9
a0a1a2a3a4a5a6a7a8a9aaabacadaeafa
b0b1b2b3b4b5b6b7b8b9babbbcbdbebfb
c0c1c2c3c4c5c6c7c8c9cacbcccdcecfc
d0d1d2d3d4d5d6d7d8d9dadbdcdddedfd
e0e1e2e3e4e5e6e7e8e9eaebecedeeefe
f0f1f2f3f4f5f6f7f8f9fafbfcfdfefff

2バイト目:
0123456789abcdef
000102030405060708090a0b0c0d0e0f0
101112131415161718191a1b1c1d1e1f1
202122232425262728292a2b2c2d2e2f2
303132333435363738393a3b3c3d3e3f3
404142434445464748494a4b4c4d4e4f4
505152535455565758595a5b5c5d5e5f5
606162636465666768696a6b6c6d6e6f6
707172737475767778797a7b7c7d7e7f7
808182838485868788898a8b8c8d8e8f8
909192939495969798999a9b9c9d9e9f9
a0a1a2a3a4a5a6a7a8a9aaabacadaeafa
b0b1b2b3b4b5b6b7b8b9babbbcbdbebfb
c0c1c2c3c4c5c6c7c8c9cacbcccdcecfc
d0d1d2d3d4d5d6d7d8d9dadbdcdddedfd
e0e1e2e3e4e5e6e7e8e9eaebecedeeefe
f0f1f2f3f4f5f6f7f8f9fafbfcfdfefff

1バイト目のオレンジ(ASCII)と、2バイト目の緑(漢字)が一部重なっているのがわかりますね。

EUC-JP
Unix系で使われていた・・・ような気がするけど、あんまり覚えてない。これもISO-2022準拠のエンコーディングですが、エスケープシーケンスは使わず、シングルシフトを使います。(しつこいですが、ISO-2022についてはこのページをみてください。)
簡単に言うと、1バイト目が0x8Eだったら次の1バイトを半角カタカナとして認識する、1バイト目が0x8Fだったら次の2バイトを補助漢字として認識する、というルールです。

使用領域
Shift_JISと違って、空白が目立ちます。空間を贅沢に使ってる分、「漢字の2バイト目がアルファベットと間違えられる」というような問題は起こりません。

デフォルト:
0123456789abcdef
000102030405060708090a0b0c0d0e0f0
101112131415161718191a1b1c1d1e1f1
202122232425262728292a2b2c2d2e2f2
303132333435363738393a3b3c3d3e3f3
404142434445464748494a4b4c4d4e4f4
505152535455565758595a5b5c5d5e5f5
606162636465666768696a6b6c6d6e6f6
707172737475767778797a7b7c7d7e7f7
808182838485868788898a8b8c8d8e8f8
909192939495969798999a9b9c9d9e9f9
a0a1a2a3a4a5a6a7a8a9aaabacadaeafa
b0b1b2b3b4b5b6b7b8b9babbbcbdbebfb
c0c1c2c3c4c5c6c7c8c9cacbcccdcecfc
d0d1d2d3d4d5d6d7d8d9dadbdcdddedfd
e0e1e2e3e4e5e6e7e8e9eaebecedeeefe
f0f1f2f3f4f5f6f7f8f9fafbfcfdfefff

1バイト目が0x8Eだったときの2バイト目:
0123456789abcdef
000102030405060708090a0b0c0d0e0f0
101112131415161718191a1b1c1d1e1f1
202122232425262728292a2b2c2d2e2f2
303132333435363738393a3b3c3d3e3f3
404142434445464748494a4b4c4d4e4f4
505152535455565758595a5b5c5d5e5f5
606162636465666768696a6b6c6d6e6f6
707172737475767778797a7b7c7d7e7f7
808182838485868788898a8b8c8d8e8f8
909192939495969798999a9b9c9d9e9f9
a0a1a2a3a4a5a6a7a8a9aaabacadaeafa
b0b1b2b3b4b5b6b7b8b9babbbcbdbebfb
c0c1c2c3c4c5c6c7c8c9cacbcccdcecfc
d0d1d2d3d4d5d6d7d8d9dadbdcdddedfd
e0e1e2e3e4e5e6e7e8e9eaebecedeeefe
f0f1f2f3f4f5f6f7f8f9fafbfcfdfefff


1バイト目が0x8Fだったときの2, 3バイト目:
0123456789abcdef
000102030405060708090a0b0c0d0e0f0
101112131415161718191a1b1c1d1e1f1
202122232425262728292a2b2c2d2e2f2
303132333435363738393a3b3c3d3e3f3
404142434445464748494a4b4c4d4e4f4
505152535455565758595a5b5c5d5e5f5
606162636465666768696a6b6c6d6e6f6
707172737475767778797a7b7c7d7e7f7
808182838485868788898a8b8c8d8e8f8
909192939495969798999a9b9c9d9e9f9
a0a1a2a3a4a5a6a7a8a9aaabacadaeafa
b0b1b2b3b4b5b6b7b8b9babbbcbdbebfb
c0c1c2c3c4c5c6c7c8c9cacbcccdcecfc
d0d1d2d3d4d5d6d7d8d9dadbdcdddedfd
e0e1e2e3e4e5e6e7e8e9eaebecedeeefe
f0f1f2f3f4f5f6f7f8f9fafbfcfdfefff


まとめ
意外にきれいな表になって嬉しい。Rubyを使ったのは半年ぶりだが、なんとなくそれっぽいコードを書けば動いてしまうのがRubyのすばらしいところだ。


余談: 元サイトの余談の引用
余談ですが、制御文字の中でDELだけ0x7Fなんていうところに飛ばされているのはなぜでしょうか? これは、データ入力媒体として紙テープを使っていたころの名残だといわれています。 紙テープでは、所定の位置に穴を空けてあるかどうかによって 2進数の1/0を区別します(SunOSのppt(6)を参照)。 1文字は7ビットですから、7つの位置の穴の有無で1文字を表します。 で、もし穴を空けるときにまちがってしまった場合、 穴をふさぐのは面倒ですので、そのまちがいを含む7ビット分全部の穴を空けて 「この字はまちがいだから削除ね」ということにしていました。 つまり、「削除」→「全ビット1」→「0x7F」というわけです。

Rubyソースコード
#!/usr/bin/ruby

ctrl = [0x00..0x1f, 0x7f..0x7f]
ascii = 0x0..0x0
#ascii = 0x20..0x7e
katakana = 0x0..0x0
#katakana = 0x21..0x5f
#katakana = 0xa1..0xdf
#katakana = 0xa1..0xdf
#kanji = []
#kanji = [0x21..0x7e]
#kanji = [0x81..0x9f, 0xe0..0xfc]
#kanji = [0x40..0x7e, 0x80..0xfc]
kanji = [0xa1..0xfe]

print <<EOS
<style type="text/css">
table.charcode {
  border-collapse: collapse;
  empty-cells: show;
}

table.charcode th,
table.charcode td {
  width: 2em;
  border: 1px solid gray;
  text-align: center;
}

td.ctrl {
  background-color: cyan;
}

td.ascii {
  background-color: orange;
}

td.katakana {
  background-color: lightgray;
}

td.kanji {
  background-color: lightgreen;
}
</style>
EOS

print %Q!<table class="charcode">\n!

print "<tr><th></th>"
for b in 0x0..0xf
  print "<th>#{sprintf("%x", b)}</th>"
end
print "</tr>"

for b0 in 0x0..0xf
  print "<tr><th>#{sprintf("%x", b0)}</th>"

  for b1 in 0x0..0xf
    b = b1 * 0x10 + b0
    
    if ctrl.any? { |r| r.include? b }
      print %Q!<td class="ctrl">#{sprintf("%02x", b)}</td>!
    elsif ascii.include? b
      print %Q!<td class="ascii">#{sprintf("%02x", b)}</td>!
    elsif katakana.include? b
      print %Q!<td class="katakana">#{sprintf("%02x", b)}</td>!
    elsif kanji.any? { |r| r.include? b }
      print %Q!<td class="kanji">#{sprintf("%02x", b)}</td>!
    else
      print %Q!<td>#{sprintf("%02x", b)}</td>!
    end
  end
  
  print "</tr>\n"
end

print "</table>\n"

2011年10月16日日曜日

末尾再帰を学ぶのは重要か? -> 重要


以前のエントリで、
と、ここまで書いてきてなんだけど、ある関数を末尾再帰にするというのは局所的な最適化なので、重要性は低いと思っている。初心者が知る必要はないし、ソフトウェアの開発初期に、部品となる関数一つ一つに対して、「これは末尾再帰にするべきか、末尾再帰になっているか」と考えるのは premature optimization というものだろう。
なのに、どうして多くの関数型言語の本もかなり序盤に末尾再帰の話を持ってくるのか疑問だ。そもそも関数型言語のような高レベル言語を使ってるのに、スタックを消費する・しない、とか考えたくない。と思うのは間違っているのだろうか? まぁ、パズルみたい (しかもたいてい簡単) なので、面白いけどね。 
と書きました。

この質問をHaskell歴の長いとある人に聞いてみたところ、「末尾再帰にしないと、スタックがのび過ぎて動かなくなることがよくある。なので初心者が末尾再帰を学ぶのは重要」とのことでした。

なるほど。速度の問題だけなら無視できるけど、動かなくなるのは困りますね。納得しました。


2011年10月7日金曜日

アイコン救急車

[Icon Ambulance の翻訳]


2008年1月6日の日曜日の朝、礼拝に参加しているときに携帯のバイブが震えた。目立たないようにそっと携帯をチェックすると、「発信者不明」だったので無視することにした。

礼拝のあと車に向かう途中で携帯のメールをチェックすると、Steve Jobsからメールが届いていた。「Vic、自宅に電話をくれ。緊急に君と話さなきゃいけないことがある。」

車に着く前にSteve Jobsに電話をかけた。私は Google で全てのモバイルアプリケーションの責任者であり、そのためSteveと定期的にやり取りをしていたが、これはその中でも思い出深い話だ。

「もしもし Steve。Vicです。」と言った。「今まで電話に出られなくてすみません。礼拝に出ていたし、発信者が不明だったので、電話に出なかったんです。」

Steveは笑ってこう言った。「Vic、神様からの電話じゃない限り、礼拝中に電話に出ちゃダメだよ。」

私は無理して笑った。Steveが平日に急いで電話してくることはよくあったが、日曜日に電話して、自宅に電話をくれなんて普通じゃない。何があったんだろうと気になっていた。

「Vic、緊急の問題があるんだ。すぐに何とかしなきゃいけない。私のチームの誰かに君をサポートするように指示したから、明日には直せると思うけど」とSteveは言う。

「GoogleのロゴをiPhoneで見ていたんだけど、アイコンが良くないんだ。Googleの2つ目のOの黄色のグラデーションがおかしい。とにかく間違ってるんだ。Gregに明日直させるけど、それでいいかい?」

もちろんそれで問題ない。その日曜の数分後、Steveから「アイコン救急車」というメールが送られてきた。私とGreg Christieにアイコンを直すよう指示していた。

11歳で Apple II と恋に落ちたものとしては、Apple製品について語りたいことは山ほどある。何十年にもわたって私の人生の一部になっている。15年間 Bill GatesのMicrosoftで働いていた時でさえ、SteveとAppleが作ってきたものとに大きな敬意を払っていた。

でも結局のところ、リーダーシップ、情熱、細部へのこだわりについて考えていると、1月の日曜日にSteve Jobsからかかってきた電話を思いだしてしまう。忘れられない教訓となった。CEOというものは、細部にこだわらなければいけない。黄色のグラデーションにまで。日曜日であっても。

私が知っている最高のリーダー Steve。あなたの健康を祈ってます。[訳注: 原文は、Steve JobsがCEOを辞任した翌日にポストされた。]

Vic

Steve Jobs


[The EconomistObituary: Steve Jobs の翻訳]

コンピュータ業界には、いや他の業界にさえも、Steve Jobsのようにプレゼンテーションができる人はいなかった。黒いステージに独りたち、「魅惑的」で「驚くほど素晴らしい」新製品を、どきどきしている観客に向けて魔法のように取り出すさまは、まさに天才エンターテイナーだった。コンピュータにできることなんて、数字を取り出して並べ替えるだけだ、とかつて彼は言った。でもそれをとても速く行えば、「魔法のような効果を生み出すんだ。」 その魔法を、美しくデザインされ、簡単に使える製品に詰め込むことに、彼は人生を捧げた。

彼は、1970年代において、普通の人にコンピュータを販売するというアイデアに可能性を見出した数少ない一人だった。黒い画面に緑の文字が表示され、フロッピーディスクがまだ柔らかかった時代に、コンピュータはすぐに誰もが使えるものになるなどと考えるのは非現実的だったが、Mr Jobsは次に何が起こるかを見ることができる、数少ないパイオニアだった。決定的だったのは、彼は、エンジニアとしてコンピュータを内側から見る才能だけではなく、ユーザーとして外側から見るという貴重な才覚も持っていたことだ。わがままな子供時代を過ごしたからだろう、と彼は語っていた。

Mr Jobsはシリコンバレーで育ち、コンピュータに取りつかれた。1960年代のティーンエイジャーは、あこがれだった Bill Hewlett に勧誘の電話をかけ、Hewlett-Packardでの夏休みのバイトを手に入れた。しかしその後、大学を中退し、インドに旅行に行き、仏教徒になり、幻覚剤を体験し、ようやく1976年のエイプリルフールに、カリフォルニアの両親のガレージでApple社を共同設立することになる。「私達の業界のほとんどの人は、幅広い体験をしたことがない」と彼は語っている。「だから、繋げられるような点を持っていないんだ。そしてとても直線的な解決策しか考えられない。」 Bill Gatesについて「若いころにLSDをやったり、ヒンズー教の僧院へ旅でもしてればもっと大きな男になっただろうにね」と言っていた。

例えば、大学を中退してカリグラフィーのクラスに出席したことで、Mr Jobs は、使い道のないタイポグラフィを愛するようになった。しかし、1984年にAppleが発売した、マウスで操作できる画期的なグラフィカルコンピュータ Macintosh にとって、様々なフォントをサポートしていることがその特徴を表していた。ウィンドウ、アイコン、そしてメニューを見たらわかるように、それは「一般人のためのコンピュータ」だった。Appleの最初の成功で富を成した Mr Jobsは新しいマシンを大量に売ろうと目論んだ。しかし、新マシンは望んだほどの成功は得られず、取締役会は Mr Jobsを追放することになった。

このどう見ても破滅的な転換は、しかし、福音となる。「自分に起こった最高の出来事だ」と Mr Jobsは後に語っている。コンピュータグラフィックスに特化した新会社 Pixar と、新しいコンピュータメーカー NeXT とを共同設立した。輝かしい第二幕は、1996年に始まった。どうしようもなくなった Apple は NeXT を買収し、その技術を Apple の新製品のコアとして組み込むためにに Mr Jobs の再登場となった。あとは歴史のとおりだ。Apple は iMac, iPod, iPhone, iPadをリリースし、(一瞬だが) 世界で最も価値のある上場企業となった。「Appleを首にならなかったら、こんなことは起こり得なかったと確信を持って言える」と Mr Jobsは2005年に言った。8月に体調悪化のためボスの座を降りたときは、歴史上最高のCEOとしてもてはやされた。そうそう、彼の副業だった Pixar は、アニメ映画の大成功作を幾つも生み出した。

振り返ってみれば、Appleでの第一幕では、Mr Jobsは時代を先取りしすぎていたといえる。初期のコンピュータは技術屋のものだった。しかし、デザインと使いやすさを強調する姿勢は、のちに優位性を生み出す。コンピュータがファッションアイテムとなり、誰もが持ち歩き、なんでもできるようになった世界においては、上品でシンプルで、コンピュータ以外の世界を知っていることが強みとなった。2011年3月にiPad2を発表したときのスピーチの最後でMr Jobsは「技術だけではダメなんだ」と言った。「文学、教養、人文科学と技術とが出会うことで、心を震わせるような成果を生み出す。」 技術の会社のトップがこんなことを言うのは珍しいが、これこそ昔からのSteve Jobsだった。

複数の学問分野をまたがるようなアプローチは、細部への極端なこだわりに支えられていた。よいタンスを作っている大工は、たとえ誰も見ないとしても、背部にベニヤ板は使わない、というのが彼の主張で、自分の製品にも同じ姿勢で取り組んだ。「夜ぐっすりと眠るためには、美学と品質が貫かれてなくてはいけない。」 彼は、最初のMacintoshを作るときに、エンジニアの利便性よりもユーザーのニーズを重視し、内部の空冷ファンを無くすべきだ、そうすれば静かになる、と主張した。週末に、Googleのエンジニアを緊急に呼び出してこう言ったりもした。「iPhoneに表示されるGoogleロゴの文字の黄色が、少し違う。」 Appleの広告のテキストを自分で書いたり、書きなおしたりすることもよくあった。

ステージ上では禅のような神秘的な仮面をかぶっていたが、Mr Jobsは独裁的で激しい癇癪持ちのマネージャーだった。それでも彼のエゴは正当化された。まだ存在しない新市場を開拓する際は、マーケットリサーチャーやフォーカスグループ [訳注: 何人かの消費者に試作品を使ってもらい、意見を製品に反映させる手法] を廃し、自分の直感を信じた。「多くの場合、それが目の前に提示されるまで、自分が何が欲しいかなんてわからないものだ」というのが彼の意見だった。彼の判断は不気味なくらい正確で、人生全体を見れば、成功は失敗を大きく上回った。Appleを作って間もないころ、あるエンジニアは、Mr Jobsが「現実をねじ曲げる力」を放射していると言ったが、彼の物事を追求する姿勢はまさにそうだった。そして最後には、コンピュータの魔法を、音楽、電話、そしてメディアを再構築する製品へと導くことで、ついに現実を変えた。青年時代に「世界中に鳴り響くような鐘を叩きたい」と言っていた男は、それを成し遂げたのだ。



[原文の訂正に伴う訂正: 週末に呼び出されたのはAppleではなくGoogleのエンジニアでした。これは2008年の事で、AppleとGoogleがライバルではなく同盟を組んでいた時の話です。]