这是另一个让我难过的Codewars Ruby问题:
描述: 在这个kata中,你必须实现一个基本转换器,它可以在任意基数/字母表之间进行转换。以下是一些预定义的字母表:
bin='01'
oct='01234567'
dec='0123456789'
hex='0123456789abcdef'
allow='abcdefghijklmnopqrstuvwxyz'
allup='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
alpha='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
alphanum='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
函数convert()应该接受输入(字符串),源字母表(字符串)和目标字母表(字符串)。您可以假设输入值始终由源字母表中的字符组成。您无需验证它。
Examples:
convert("15", dec, bin) #should return "1111"
convert("15", dec, oct) #should return "17"
convert("1010", bin, dec) #should return "10"
convert("1010", bin, hex) #should return "a"
convert("0", dec, alpha) #should return "a"
convert("27", dec, allow) #should return "bb"
convert("hello", allow, hex) #should return "320048"
附加说明:
最大输入值始终可以编码为数字,而不会损失JavaScript中的精度。在Haskell中,Int的中间结果可能很大。 该函数必须适用于任何任意字母,而不仅仅是预定义的字母。 您不必考虑负数。
我已经玩了几天,并设法让数字基础转换部分工作。这是它的字母部分,我无法弄清楚如何接近,我的大脑厌倦了尝试。这是我的代码:
def convert(input, source, target)
bases = {
:bin => '01',
:oct => '01234567',
:dec => '0123456789',
:hex => '0123456789abcdef',
:allow => 'abcdefghijklmnopqrstuvwxyz',
:allup => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
:alpha => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
:alphanum => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
}
base_from , base_to = 0
src_num_switch = 1 if source == bases[:bin] || [:oct] || [:dec] || [:hex]
tgt_num_switch = 1 if target == bases[:bin] || [:oct] || [:dec] || [:hex]
src_num_switch = 0 if source == bases[:allow] || [:allup] || [:alpha] || [:alphanum]
tgt_num_switch = 0 if target == bases[:allow] || [:allup] || [:alpha] || [:alphanum]
if source == bases[:bin] then base_from = 2
elsif source == bases[:oct] then base_from = 8
elsif source == bases[:dec] then base_from = 10
elsif source == bases[:hex] then base_from = 16
elsif source == bases[:allow] then base_from = 13
elsif source == bases[:allup] then base_from = 13
elsif source == bases[:alpha] then base_from = 13
elsif source == bases[:alphanum] then base_from = 13
else puts ":( no source match found :("
end
if target == bases[:bin] then puts base_to = 2
elsif target == bases[:oct] then base_to = 8
elsif target == bases[:dec] then base_to = 10
elsif target == bases[:hex] then base_to = 16
elsif target == bases[:allow] then base_to = 13
elsif target == bases[:allup] then base_to = 13
elsif target == bases[:alpha] then base_to = 13
elsif target == bases[:alphanum] then base_to = 13
else puts ":( no target match found :("
end
if base_from == base_to then
return input
elsif src_num_switch == 1 && tgt_num_switch == 1 then
return Integer(input, base_from).to_s(base_to)
elsif src_num_switch == 0 && tgt_num_switch == 0 then
return Integer(input, base_from).to_s(base_to)
# ### # :::::::::::::::::::::::::::::::::::::::::::::
else
puts "ouch, something broke"
end
end
我已经把所有内容都归结为“#####”部分为我工作了。谁能让我知道如何做alpha-base部分?我尝试过以下但没有运气:
if base_from == base_to then return input
elsif src_num_switch == 1 && tgt_num_switch == 1 then
return Integer(input, base_from).to_s(base_to)
elsif src_num_switch == 1 && tgt_num_switch == 0 then
if target == bases[:allup] then return bases[input.index].to_s.upcase
elsif target == bases[:allow] then return bases[input.index].to_s.downcase
end
end
elsif src_num_switch == 0 && tgt_num_switch == 1 then
return input.index.to_s(base_to)
elsif src_num_switch == 0 && tgt_num_switch == 0 then
return Integer(input, base_from).to_s(base_to)
else
puts "ouch, something broke"
end
这也是:
elsif src_num_switch == 1 && tgt_num_switch == 0 then # number-base to alphanumeric-base
if target == bases[:allup] then
return bases[input.index].to_s.upcase
elsif target == bases[:allow] then
return bases[input.index].to_s.downcase
end
elsif src_num_switch == 0 && tgt_num_switch == 1 then # alpha-base to number-base
return input.index.to_s(base_to)
答案 0 :(得分:1)
可能有一个非常聪明的内置Ruby解决方案,但我会猜测基于自定义字母表描述没有的数字系统。因此,我没有直接回答如何完成您的代码,但我建议采用略有不同的策略。
任何数字系统都可以从十进制系统转换,如下所示:
vals_in_system = system.length
output_in_system = []
while (decimal_num != 0)
index_of_next_val = decimal_num % system.length
output_in_system.unshift(system[index_of_next_val])
decimal_num = decimal_num / vals_in_system # truncating is desired here
end
这有点棘手。该算法首先尝试确定它必须放在最后位置的值(在您使用的任何数字系统中具有最大粒度)。例如。如果你用十进制表示12(是的,它已经是,但是使用这个算法),2必须进入最后一个位置 - 没有你放在十位或更高位置的数字将帮助你代表12.如果你是以二进制表示3,1必须进入二进制的最后位置 - 你放在下一个位置的任何东西都不会让你进入3.一旦它确定了它,就可以除以基数,这将离开你用你用来计算剩余位置的数字。例如,如果你用十进制表示123,除以10(十进制基数),截断就会得到12. 12是原始数字的表示,除了最终位置(通过除以基数来切断) )。 (我意识到这不是最明确的解释,所以如果你有疑问请告诉我。)一些例子:
E.g。十进制数15可以转换为二进制数:
那有点无聊,你只需要1111.尝试一些更有趣的东西,比如10:
你得到1010,二进制确实是10。您可以使用任何这些字母表执行此操作。
同样,任何数字系统都可以通过相反的方式转换为小数:
vals_in_system = from.length
output_in_decimal = 0
val.each_char do |next_val|
output_in_decimal *= vals_in_system
output_in_decimal += from.index(next_val)
end
这比" from decimal"更容易理解。算法。考虑是否要将其应用于十进制数123.此算法基本上是执行此等式
((1 * 10) + 2) * 10) + 3
或者,更容易阅读:
1 * (10 * 10) + 2 * (10) + 3
只是迭代。它适用于其他数字系统,通过用数字系统的基数替换10(即数字系统包含的值的数量)。它所做的唯一其他魔法就是使用.index
将数字系统中的值转换为十进制数。
E.g。转换" bcdl"来自他们的"允许"系统。使用0指数,b =第1位,c = 2,d = 3,l = 11th
一旦你有了十进制的转换器,你可以编写一个非常简单的包装器来处理每种情况(我将DEC
放在一个常量中以使其在此方法中可见,它与dec
):
def convert(val, from, to)
case
when from == to then val
when from == DEC then convert_from_dec(val, to)
when to == DEC then convert_to_dec(val, from)
else
convert_from_dec(convert_to_dec(val, from), to)
end
end
在那之后,你大部分时间都要处理边缘情况。
正如我所说的,不是对你的问题的直接回答,但似乎你必须对alpha编号系统使用这种通用方法,此时你也可以将它用于所有事情:)< / p>
答案 1 :(得分:0)
老实说,我试着不看alexcavalli的解决方案,但最终得出了完全相同的算法,只是代码不同。因此,为了解释它为什么起作用,请看他更多解释的答案。这只是唯一的代码,如果以base_converter.rb名称保存它,则可以这样编写:
$ ruby ../base_converer.rb 123 hex dec #=> 291
bases = {
bin: '01',
oct: '01234567',
dec: '0123456789',
hex: '0123456789abcdef',
allow: 'abcdefghijklmnopqrstuvwxyz',
allup: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
alpha: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
alphanum: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
}
def to_int(num, src)
src_map = src.split('').map.with_index.to_h
num.reverse.each_char.with_index.sum{ |c, i| src_map[c] * (src.size ** i) }
end
def from_int(num, dst)
res = []
while num > 0
res << dst[num % dst.size]
num /= dst.size
end
res.join.reverse
end
def convert(num, src, dst)
from_int(to_int(num, src), dst)
end
if ARGV.size > 2
puts convert(ARGV[0], bases[ARGV[1].to_sym], bases[ARGV[2].to_sym])
end