To był rodzaj interesującej kwestii, więc poszedłem trochę za burtę:
class Integer
def to_base(base=10)
return [0] if zero?
raise ArgumentError, 'base must be greater than zero' unless base > 0
num = abs
return [1] * num if base == 1
[].tap do |digits|
while num > 0
digits.unshift num % base
num /= base
end
end
end
end
Działa to dla dowolnych podstawach. Działa tylko dla liczb całkowitych, chociaż nie ma powodu, dla którego nie mógłby być rozszerzony na pracę z dowolną liczbą. Ponadto ignoruje znak liczby. Ponownie, nie ma powodu, dla którego musi to robić, ale głównie nie chciałem wymyślać konwencji o zwrocie znaku w wartości zwracanej.
class Integer
old_to_s = instance_method(:to_s)
define_method :to_s do |base=10, mapping=nil, sep=''|
return old_to_s.bind(self).(base) unless mapping || base > 36
mapping ||= 'abcdefghijklmnopqrstuvwxyz'
return to_base(base).map {|digit| mapping[digit].to_s }.join(sep)
end
end
[Fixnum, Bignum].each do |klass|
old_to_s = klass.instance_method(:to_s)
klass.send :define_method, :to_s do |base=10, mapping=nil, sep=''|
return old_to_s.bind(self).(base) unless mapping || base > 36
return super(base, mapping, sep) if mapping
return super(base)
end
end
ja również rozszerzyć metodę to_s
tak, że działa z baz większych niż 36. Jeśli chcesz korzystać z bazy większa niż 36, trzeba przejść w obiekcie odwzorowania, która odwzorowuje „cyfry” na smyczki . (Cóż, w rzeczywistości wszystko, co jest wymagane, to dostarczenie obiektu, który odpowiada na []
i zwraca coś, co odpowiada na to_s
. Tak więc łańcuch jest idealny, ale np. Działa również tablica liczb całkowitych.)
To także akceptuje opcjonalny separator, który służy do oddzielania cyfr.
Na przykład, umożliwia formatowanie adres IPv4, traktując je jako a-bazowej 256 numer i za pomocą tożsamości dla umiejscowienia i '.'
jako separator:
2_078_934_278.to_s(256, Array.new(256) {|i| i }, '.') # => '123.234.5.6'
oto (niekompletny) testsuite :
require 'test/unit'
class TestBaseConversion < Test::Unit::TestCase
def test_that_83992_in_base_85_is_11_53_12
assert_equal [11, 53, 12], 83992.to_base(85)
end
def test_that_83992_in_base_37_is_1_24_13_2
assert_equal [1, 24, 13, 2], 83992.to_base(37)
end
def test_that_84026_in_base_37_is_1_24_13_36
assert_equal [1, 24, 13, 36], 84026.to_base(37)
end
def test_that_0_in_any_base_is_0
100.times do |base|
assert_equal [0], 0.to_base(base)
assert_equal [0], 0.to_base(1 << base)
assert_equal [0], 0.to_base(base << base)
end
end
def test_that_84026_in_base_37_prints_1od_
assert_equal '1od_', 84026.to_s(37, 'abcdefghijklmnopqrstuvwxyz_')
end
def test_that_ip_address_formatting_works
addr = 2_078_934_278
assert_equal '123.234.5.6', addr.to_s(256, (0..255).to_a, '.')
assert_equal '123.234.5.6', addr.to_s(256, Array.new(256) {|i| i}, '.')
end
def test_that_old_to_s_still_works
assert_equal '84026', 84026.to_s
assert_equal '1su2', 84026.to_s(36)
end
end
Jorg, wspaniała praca. –
"Mały" za burtę? XD Przy okazji, to jest niesamowite :) – Doorknob
Jorg, nie chcesz opublikować tego jako klejnot? – sNiCKY