Eso era una especie de una pregunta interesante, así que fui un poco por la borda:
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
Esto funciona para bases arbitrarias. Solo funciona para enteros, aunque no hay ninguna razón por la cual no se pueda extender para trabajar con cualquier número arbitrario. Además, ignora el signo del número. Una vez más, no hay ninguna razón por la cual debe hacer eso, pero principalmente no quería tener que presentar una convención para devolver el signo en el valor de retorno.
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
También extendieron el método to_s
para que funcione con bases mayores de 36. Si desea utilizar una base mayor que 36, usted tiene que pasar en un objeto de asignación que asigna los "dígitos" para cuerdas . (Bueno, en realidad, todo lo que se requiere es que proporcione un objeto que responda al []
y devuelva algo que responda a to_s
. Por lo tanto, una cadena es perfecta, pero también funciona una matriz de enteros)
También acepta un separador opcional, que se usa para separar los dígitos.
Por ejemplo, esto permite dar formato a una dirección IPv4 tratándola como base 256 el número y el uso de la identidad para el mapeo y '.'
como separador:
2_078_934_278.to_s(256, Array.new(256) {|i| i }, '.') # => '123.234.5.6'
He aquí un banco de pruebas (incompleto) :
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, trabajo fabuloso. –
¿Un "pequeño" por la borda? XD Por cierto, eso es increíble :) – Doorknob
Jorg, ¿no quieres publicar esto como una joya? – sNiCKY