[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[dennou-ruby:000203] [REQ] Integer#{hex,dec,oct,bin}, String#bin
ごとけんです
# 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