Ruby

时间:2015-04-23 15:06:26

标签: ruby

这是另一个让我难过的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)        

2 个答案:

答案 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可以转换为二进制数:

  1. 15%2 = 1#last position
  2. 15/2 = 7
  3. 7%2 = 1#在最后一个位置旁边
  4. 7/2 = 3
  5. 3%2 = 1#3rd to last position
  6. 3/2 = 1
  7. 1%2 = 1#第4到最后位置
  8. 1/2 = 0#stop
  9. 那有点无聊,你只需要1111.尝试一些更有趣的东西,比如10:

    1. 10%2 = 0#last position
    2. 10/2 = 5
    3. 5%2 = 1#在最后一个位置旁边
    4. 5/2 = 2
    5. 2%2 = 0#3rd to last position
    6. 2/2 = 1
    7. 1%2 = 1#第4到最后位置
    8. 1/2 = 0#stop
    9. 你得到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

      1. 从0开始
      2. 乘以数字系统库,即26(小写字母表中的26个字母)= 0
      3. 添加b(1)=>的小数值1
      4. 1 * 26 = 26
      5. 添加c(2)=>的小数值28
      6. 28 * 26 => 728
      7. 添加d(3)=>的小数值731
      8. 731 * 26 => 19006
      9. 添加l(11)=>的小数值19017年" bcdl"。
      10. 的十进制表示法

        把它放在一起

        一旦你有了十进制的转换器,你可以编写一个非常简单的包装器来处理每种情况(我将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