Ruby の記事によく見られる誤りとしてこれがある。
本題に入る前に少し用語を整理しておこう。
まず
a = 17
という何の変哲もないプログラムを考える。
これはローカル変数 a
に数値 17
を代入している。
まず,この 17
のことを「数字」と表現した記事が非常に多い。
「数値」と「数字」は違う。(「数」と「数値」は基本的に同義1なので,この記事では区別しないことにする)
数字と言うのは,数を書き表すときに用いる記号のこと(ただし,負符号〔マイナス記号〕とか小数点などは数字に含めない)。
10 進法という記数法であれば,0 から 9 までの記号が「数字」だ。
上の例でいうと,ソースコード上の 1
と 7
は数字だが,a
に代入されるのは数字ではなく数である。
非学術的な言語使用では比喩表現で数のことを「数字」と呼ぶことがある。
決算書を見て「いい数字が出たね」というように。
しかし,プログラミングの話をしているときは,数と数字をはっきり区別したい。
さて,本題に入ろう。
2 進法で「1101」と書かれた数は 10 進法ではどうなるだろう。
以下のプログラムで簡単に分かる。
p "1101".to_i(2) # => 13
答えは「13」。
このプログラムは String#to_i というメソッドを用いている。
文字列オブジェクトを整数オブジェクトに変換するものだが,引数で基数を指定することができる(基数というのは,N 進法の N のこと)。デフォルト値は 10 だ。
いまの場合,引数に 2 を与えているので,レシーバーの文字列を 2 進数とみなして整数オブジェクトを作る。
ここまではよいのだが,どうやらかなり多くの人がこの "1101".to_i(2)
を見て「2 進数を 10 進数に変換している」と勘違いしているぽい。
いやまあ,誤解するのも無理はない。だって,上のプログラムを実行したら「13」て表示されるんだもんね。「13」は 2 進数「1101」に対応する 10 進数で間違いないもの。
ではいったい何が「誤り」だというのか?
もう一度「N 進法」「N 進数」という言葉について考えてみよう。
N 進法は N 種類の数字を用いる位取り記数法だ。記数法というのは数の表記方法のこと。
N 進数は N 進法に基づいて数を表したもの。
しかるに,to_i
メソッドの返り値は文字列ではない。Integer オブジェクトだ。よって,「数字を並べたもの」ではない。2 進数でも 10 進数でもないのだ。だから to_i
は「10 進数に変換するメソッド」ではない。
Integer オブジェクトは処理系内部では 2 進法に基づく表現となっているが,それは処理系の都合であって,ユーザーの知ったことではない2。
では,一体なぜ上のプログラムは 10 進数の「13」を表示したのだろうか。
それは,こういう仕組みだ。
"1101".to_i(2)
は 13 という数に対応する Integer オブジェクトを返す。
筆者がいま「13 という数」と書いたのは,とくに断りがない限り我々は数を 10 進法で表す習慣があるからそう書いたまでのこと。くどいようだが,この Integer オブジェクトは何らかの意味で「1」と「3」を並べたものでは断じてない。
さて,その Integer オブジェクトが p
メソッドの引数として渡される。
p
メソッドは,与えられたオブジェクトを inspect
メソッドで文字列化して出力する。
Integer
の inspect
メソッドは,引数を与えない場合,自身を 10 進法に基づく数字列(つまり 10 進数)という String オブジェクトに変換する。だから,今の場合,"13"
という文字列が得られる。
要するに,to_i
によって 10 進数に変換されたのではなく,それを表示するときに 10 進数になったというわけ。