読者です 読者をやめる 読者になる 読者になる

定食屋おろポン

おろしポン酢と青ネギはかけ放題です

バーコードのチェックサムとHaskellの練習

コンビニの商品にも、スーパーの商品にも、たいていの売り物にはバーコードがついていますね。 すだれハゲみたいなアレです。

http://ja.wikipedia.org/wiki/バーコード

日本の商品に使用されるバーコードは JANコード というもので、8桁あるいは13桁の数字を表しています。

バーコードの最後の1桁は「チェックデジット」というものです。 ざっくりいうと、8桁のバーコードの場合、最後の1桁は残りの7桁を全て足しあわせた数の'一の位'です。 *1

92384781というバーコードであれば、1チェックデジットです。9+2+3+8+4+7+8 = 41なので、41の'一の位'である1がバーコードの最後に来るわけです。

...というようなことを風のうわさで聞きました。

「バーコードにはチェックデジットなるものがついてるらしいよ」、と。

では、なぜバーコードにはチェックデジットなんて付いているのでしょうか。 13桁の数値なら、約10兆個の商品コードが生成できます。 そのうち1桁をチェックデジットに使ってしまうと、1兆個の商品コードしか作れないじゃないですか!梅雨明けてないじゃないスか!やだー!


ということでウンウン唸りながら考えて、しばらくしてポンと膝をたたきました。*2

「ヤツは、バーコードの誤認識を防ぐためにあるんだな!」

ということに気が付いたのです。

コンビニとか、スーパーって、ピッピがあるじゃないですか。分かりませんか?あの、赤い光が出ててピッピ言ってるアレです。

あいつが「ピッ」って言ったら、バーコードを読み込んだ証拠です。「なかなかピッて言わないなこいつ」ってことはあっても、「ピッって言ったくせに読み込んでないじゃないスかー!やだー!」ってことはありません。

でも、商品のバーコードってわりとヨレヨレだったりするじゃないですか。掠れてたり、汚れてたり、曲がってたり、濡れてたり。

92384781ってバーコードなのに、間違えて72384781って読み込んでしまったりしそうじゃないですか。

でも、コンビニの店員さんに「すみません、こいつがピッって言ったから『この商品はすでにインプットされた』と誤解されてしまったかと思います。しかし、今のはちょっとしたミスでして、もう一度読み込むのでちょっとお待ちいただけますか、このピッピはあとでよーく叱っておきますので、何卒ご理解とご協力をお願い致します」なんて謝られたことはありませんよね。僕はありません。

読み込み率100%です。すごいです。ピッピからピクシーに進化させてやりたいくらいです。ダッシュでおつきみやまでつきのいしを拾ってくる勢いです。

そこで、チェックデジットの出番です。 92384781ってバーコードを間違えて72384781って読んでしまっても、そこで「ピッ」とは言わないんです。 グッと我慢して、「こいつのチェックデジット合ってるかな」って確認するんです。

72384781の場合、7+2+3+8+4+7+8=39なのでチェックデジット39の'一の位'である9ですが、72384781の最後のひとけた目は1になっている。

「であえー!であえー!クセモノだー!!」

となり、ピッピは「てへっ間違えちゃった」とつぶやきながら、正しいバーコードを読むために読み込みを継続するんですね。

なるほどーなるほどー。

最初に考えた人はすごいですねー。


ということで、チェックデジット付きのバーコードをランダムに吐き出すHaskellプログラムを書きました。

こわーいこわーいIOモナドの練習です。

「ある関数を同じ引数で呼び出せば、必ず同じ値が返ってくる」という参照透明性を持つHaskellでは、「ランダムな値」というのはクセモノです。 こういうクセモノに対処するには、嫌でもIOモナドと付き合う必要があります。 今回は、

  • なるべく汚染されたIOモナドの使用を最小限にして、バーコード生成プログラムを書く
  • 無限リストでバーコードを吐き出す

というのを目標にバーコードを生成してみました。

8桁のアルファベットで、チェックデジットにはXORを使用、O0と読み間違えやすいので省いています。

感想としては「nub遅ぇ〜!!」「IOモナド難しい..Rubyで書いたらゆるふわ楽勝なのに..」ってかんじですね。 haskellのnubはなんでこんなに遅いのか、オフトゥンの中で考えてみようと思います。

gist8540769

*1:正確なことはのチェックデジット計算方法を御覧ください。

*2:コトバのアヤです。「コーヒー吹いたwww」と似たようなものです。