今回から「BK通信」(ビーケーツウシン)と題して、連載することになった高林と申します。以前連載していた「プログラミングの光景」ではデバッグ、コードレビューといった大きなテーマを取り上げましたが、今回の連載では日常的に遭遇するチマチマした「バッドノウハウ」(Bad Knowhow)について書いていきたいと思います。
バッドノウハウとは?
バッドノウハウとは、筆者が2003年に作った造語です。元の定義は以下のようなものです。
計算機を使っていると、何でこんなことを覚えないといけないのだろうか、とストレスを感じつつも、それを覚えないとソフトウェアを使いこなすことができないためにしぶしぶ覚えなければならない、といった類いのノウハウは多い。そうした雑多なノウハウのことを、本来は知りたくもないノウハウという意味で、私はバッドノウハウと呼んでいる。
一方、「はてなキーワード」にある定義は簡潔ですっきりしています。
ソフトウェアなどを使いこなすために、ストレスを感じながらもしぶしぶ覚えなければならないようなノウハウ。
本連載では筆者が遭遇して「こ、これはバッド...」と絶句したバッドノウハウ(以下、BK)を紹介していきたいと思います。「うわ、これバッドだなあ」とか「オレもこれにはまった!」などと共感して楽しんでもらえれば何よりです。
JavaScriptのsort関数
JavaScriptの配列にはsort関数が定義されています。配列をソートしたければこの関数を呼べばOKと思いきや、予期せぬ挙動に悩まされました。何でだろうと思って調べると、数値の配列をソートしても文字列の順序でソートされているのが原因でした(図1)。
図1 数値の配列をソートしたつもりが...
% js # SpiderMonkeyのインタープリタを実行
js> [3, 1, 10, 2].sort()
1,10,2,3
[3, 1, 10, 2
]をソートしたら当然[1, 2, 3, 10
]になるはず、と思いきや、意外な挙動です。ECMAスクリプトの仕様書を読むと、sort( )関数に比較関数が渡されていない場合は、各要素は暗黙的に文字列に変換されると書かれています。
というわけで、比較関数を渡してやれば解決します(図2)。
知人に聞いてみると、JavaScriptプログラマにとっては常識とのことでした。でも、これ、はじめて遭遇したときは誰でも「バッド...」とのけぞるんじゃないでしょうか。
図2 比較関数を渡して解決
js> [3, 1, 10, 2].sort(function(a, b)
{ return a - b; })
1,2,3,10

JavaScriptの配列末尾のカンマ
これもまたJavaScriptプログラマには常識という話ですが、あるとき、配列を初期化するときの末尾のカンマの解釈がInternet Explorerと他のブラウザで異なるという問題ではまりました(リスト1)。
リスト1 配列末尾のカンマが...
var a = [1,2,3,]
alert(a.length);
リスト1のスクリプトを実行するとIE(IE 7で確認)では4を、その他のブラウザでは3を返します。JavaScriptでは配列の要素を省略して初期化することを許しておりa = [1,,3]
のような書き方が可能です(この場合のa.lengthは3でa[1]は空)。そう考えると一見、IEのほうが正しそうですが、ECMAスクリプトの仕様書をよく読むと、最後のカンマは解釈が異なり、結局、IEのほうが間違っているようです。
配列を初期化するコードを自動生成する場合、最後に余分なカンマを付けて出力するほうが簡単なのですが、IEの存在を考えると、律儀にカンマを取り除いてやる必要があります。バッドですね。

月のオリジン
JavaScriptのDateは日付と時間の処理するのに使える便利なオブジェクトです。が、最初に使ったときは月が1つずれるという問題にはまりました。(図3)
図3 月が1つずつずれる...
js> (new Date).toString()
Sun Mar 02 2008 21:31:43 GMT+0900 (JST)
js> (new Date).getMonth()
2
このように、3月のときはgetMonth( )は2を返します。つまり、Dateオブジェクトの月は0から始まっています。なんでこんなバッドな仕様なんだ、ほかの言語はどうなっているんだ、と思って以前に調べたところ、表1のような結果になりました。
表1 各種言語の月のオリジン
言語(ライブラリ) | 月のオリジン |
C(localtime) | 0 |
Java(Calendar) | 0 |
Perl(localtime) | 0 |
JavaScript(Date) | 0 |
Ruby(Time) | 1 |
Ruby(Date) | 1 |
Perl(DateTime) | 1 |
Perl(Time::Piece) | 1 |
Python(time) | 1
|
Python(datetime) | 1 |
PHP(getdate) | 1 |
Scheme(srfi 19) | 1 |
.NET(DateTime) | 1 |
月を数字で表記する日本人にとっては1から始まるほうが自然ですが、Jan、Feb、Marなどと、おもに名前で表記する英語圏の人にとっては0から始まってもあまり気にならないのかもしれません。むしろ、配列の添え字に使いやすいので0のほうが好都合な面もありそうです。
いずれにしても、使う言語やライブラリによって月のはじまりが0なのか1なのか覚えるか調べないといけないのは、まさにBKといえます。

まとめ
今回はJavaScriptのBKを3つ紹介しました。3つとも自分がはまった落とし穴ですが、選ぶとすればsort関数の挙動が一番、バッド度が高い(はまりやすい)んじゃないかと思います。バッドバッド、といいつつも、実はなんだかんだで楽しんでいたりするのですが。