Это был довольно интересный вопрос, поэтому я немного переборщил:
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
Это работает для произвольных оснований.Он работает только для целых чисел, хотя нет никаких причин, почему он не может быть расширен для работы с любым произвольным числом.Кроме того, он игнорирует знак числа.Опять же, нет никаких причин, по которым должен делать это, но в основном я не хотел придумывать соглашение о возвращении знака в возвращаемом значении.
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 ||= '0123456789abcdefghijklmnopqrstuvwxyz'
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
Я также расширил метод to_s
, чтобы он работал с базами больше 36. Если вы хотите использовать базу больше 36, вы должны передать объект отображения, который отображает «цифры» в строки.(Ну, на самом деле, все, что требуется, это предоставить объект, который отвечает на []
и возвращает что-то, что отвечает на to_s
. Итак, строка идеальна, но, например, массив целых чисел также работает.)
Он также принимает необязательный разделитель, который используется для разделения цифр.
Например, это позволяет форматировать адрес IPv4, обрабатывая его как число base-256 и используя идентификатор дляотображение и '.'
в качестве разделителя:
2_078_934_278.to_s(256, Array.new(256) {|i| i }, '.') # => '123.234.5.6'
Вот (неполный) набор тестов:
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, '0123456789abcdefghijklmnopqrstuvwxyz_')
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