ごとけんです

# ruby-list にしようかと一瞬思ったけど ruby-dev にします。

何度か出た話題ですが、やはり、Integer が自分をフォーマットす
るメソッドはないとメソッドチェーンを続ける際などしばしば不便
です。当時 [ruby-dev:4853] は、新井さんがかなり頑張ったので
すがまつもとさんをおとすにはいたりませんでした。ぼくもあんま
り元気ないのでなかなか議論を深める自信はありませんがとりあえ
ず提案してみます。

まず当時何がほしいかってのが問題でしたが、要するにほしいのは
いろんなものが追加できる高級な format ではなく単にn進数表記
をその場でほしいのだということをここでは主張したいと思います。
ただし n = 2,8,10,16 でよいでしょう。

それから、名前ですが、ここは素直に bin, oct, dec, hex を提案
します。to_なんとかでも悪くはないですが、考え出すとキリがあ
りません。Integer#hex の意味は自明でしょう(強引にいく作戦)。

そして、ここが本命ですが当時触れられなかった問題として区切り
というのがあります。たとえば12億6845万2035をRubyのリテラルで
は 12_6845_2035 と書けますが、逆にIntegerをこのような表記に
するのはやや厄介です。しかし、これは format の拡張で対処する
問題とも思えません(書式の拡張が難儀なので)。

そこで、つぎのようなのを考えました(というか使ってます)。

 123891790200.hex              #=> "1cd886b178b"
 123891790200.hex("22")        #=> "            1cd886b178"
 123891790200.hex("022")       #=> "0000000000001cd886b178"
 123891790200.hex("22",4)      #=> "00_0000_0000_001c_d886_b178"
 123891790200.hex("+22",4)     #=> "+0_0000_0000_001c_d886_b178"
 (-123891790200).hex("+22",4)  #=> "-0_0000_0000_001c_d886_b178"

つまり、第1引数で符号、全体の幅とゼロパディングを指定し、第2
引数で区切り幅を指定するわけです。これがあると助かることはか
なり多いし、名前も機能も最小限だと思います。また、整数が自分
のn進数表現やその区切り方を受け持つというのは自然なことです。

てなかんじの、共通のインターフェイスをもつ Integer のメソッ
ドとして2,8,10,16進数表現 bin, oct, dec, hex を要求します。

それから、16進数、10進数、8進数の文字列から整数に変換するメ
ソッドはあるのに2進数文字列からの変換メソッドを得る方法がな
いのも個人的に不便なので、String#bin もリクエストします。

対称性のために String#to_i の別名として String#dec もあると
良いと思いますが、これは特に要求しません。

-- gotoken

# いちお普段使ってるのをつけときます。

# numrepres.rb class Integer def bin(fmt = "", width = 0, sep = ?_) represent(:bin, fmt, sep, width) end def oct(fmt = "", width = 0, sep = ?_) represent(:oct, fmt, sep, width) end def dec(fmt = "", width = 0, sep = ?_) represent(:dec, fmt, sep, width) end def hex(fmt = "", width = 0, sep = ?_) represent(:hex, fmt, sep, width) end private def represent(base, fmt, sep, width) case base when :bin, :oct, :dec fmt =~ /\A\+?(?:[0]?[1-9][0-9]*)?\Z/ or raise ArgumentError, "invalid format `#{fmt}'" when :hex fmt =~ /\A\+?(?:[0]?[1-9][0-9]*)?[xX]?\Z/ or raise ArgumentError, "invalid format `#{fmt}'" end case base when :bin buf = "%#{fmt}b" % self when :oct buf = "%#{fmt}o" % self when :dec buf = "%#{fmt}d" % self when :hex fmt += "x" unless /[xX]\Z/ =~ fmt buf = "%#{fmt}" % self end if width > 0 buf[0].chr + buf[1..-1].reverse!.gsub(/#{"."*width}/,"\\&#{sep.chr}").reverse! else buf end end end class String def bin eval("0b" + (scan(/\A[01][01_]*/)[0])) end alias dec to_i end