kan2num.rb


そもそも、たのしいRubyに七千八百二十三とかを7823とかに変換するプログラムを作れというのが練習問題にあって、回答がネット上にありました。

def kan2num(string)
  digit4 = digit3 = digit2 = digit1 = "0"
  nstring = string.dup
  nstring.gsub!(/一/, "1")
  nstring.gsub!(/二/, "2")
  nstring.gsub!(/三/, "3")
  nstring.gsub!(/四/, "4")
  nstring.gsub!(/五/, "5")
  nstring.gsub!(/六/, "6")
  nstring.gsub!(/七/, "7")
  nstring.gsub!(/八/, "8")
  nstring.gsub!(/九/, "9")
  if nstring =~ /((\d)?千)?((\d)?百)?((\d)?十)?(\d)?$/
    if $1
      digit4 = $2 || "1"
    end
    if $3
      digit3 = $4 || "1"
    end
    if $5
      digit2 = $6 || "1"
    end
    digit1 = $7 || "0"
  end
  return (digit4+digit3+digit2+digit1).to_i
end

いろいろ感じて、感じたことが正しいかどうかは別にして、一応書くと。
1.stringのまま扱っているのが2バイト文字はやはりいったんarrayにすべきではないかと思う
2.一二三四もarrayで持って、mapなりzapなりした方がいいと思う
3.正規表現で切り分けるのはかっこよすぎる
4.万億兆京までは対応すべき
5.recursiveにならないか

次にネットで見つけたのが次。

def kan2num(kan)
  i = nil
  sum = 0
  kan.scan(/./).each do |e|
    if e == "〇"
      i = 0
    elsif e == "一"
      i = 1
    elsif e == "二"
      i = 2
    elsif e == "三"
      i = 3
    elsif e == "四"
      i = 4
    elsif e == "五"
      i = 5
    elsif e == "六"
      i = 6
    elsif e == "七"
      i = 7
    elsif e == "八"
      i = 8
    elsif e == "九"
      i = 9
    elsif e == "千"
      if i
        i = i*1000
      else
        i = 1000
      end
    sum += i
    i = nil
    elsif e == "百"
      if i
        i = i*100
      else
        i = 100
      end
    sum += i
    i = nil
    elsif e == "十"
      if i
        i = i*10
      else
        i = 10
      end
    sum += i
    i = nil
    end
  end
  return sum+=i
end

えっと、というわけでたとえば文字列が
kanstring="七千八百二十三"
とすると、まず、
kanary(漢字のarryのつもり)=kanstring.split(//)
なのでkanary2num(kanary)という関数を作る。
京兆億万とすると、切り分けて
kanary1京kanary2兆kanary3億kanary4億kanary5
として、答えを
kanary1*10**16 +
kanary2*10**12 +
kanary3*10**8 +
kanary4*10**4 +
kanary5
とするのでどうか。
4桁部分はとりあえずkanary2num_s(スモールの意味)とするのでどうか。
全体的に命名法に難アリですね。kan2numがいいし、やはり最初は引数はstringかな。そこから呼び出すものをf_kan2numとかにするのはどうか。4桁漢字=>numの意味。

def f_kan2num(array)
end

def kan2num(string)
end

ととりあえずスタブを置く。

次にテスト。

  def test_kan2num
    assert_equal(7516, kan2num("七千五百十六"))
    assert_equal(1516, kan2num("千五百十六"))
    assert_equal(30, kan2num("三十"))
    assert_equal(10000, kan2num("万"))
    assert_equal(75160001, kan2num("七千五百十六万一"))
  end
  def test_f_kan2num
    assert_equal(0, kanary2num([]))
    assert_equal(30, kanary2num(["三","十"]))
  end

で、今。

require 'kconv'
$KCODE='s'

def f_kan2num(array)
  ary = array.dup
  i = 0
  sum = 0
  kan = ["〇","一","二","三","四","五","六","七","八","九"]
  ary = ary.map{|item| kan.index(item) ? kan.index(item) : item}
  ary.each do |element|
  case element
    when 0..9 then
      i = element
    when  "千" then 
      (i != 0) ? (i = i*1000) : (i = 1000)
      sum += i
      i = 0
    when  "百"
      (i != 0) ? (i = i*100) : (i = 100)
      sum += i
      i = 0
    when  "十"
      (i != 0) ? (i = i*10) : (i = 10)
      sum += i
      i = 0
    end
  end
  sum += i
  return sum
end

def kan2num(string)
  kanary = string.split(//)
  sum = 0
  if (index_kei = kanary.index("京"))
    kanary_temp = kanary[0...index_kei]
    num = f_kan2num(kanary_temp)
    (num != 0) ? num = num * 10**16 : num = 10**16
    sum += num
    kanary = kanary[index_kei+1..-1]
  end
  if (index_chou = kanary.index("兆"))
    kanary_temp = kanary[0...index_chou]
    num = f_kan2num(kanary_temp)
    (num != 0) ? num = num * 10**12 : num = 10**12
    sum += num
    kanary = kanary[index_chou+1..-1]
  end
  if (index_oku  = kanary.index("億"))
    kanary_temp = kanary[0...index_oku]
    num = f_kan2num(kanary_temp)
    (num != 0) ? num = num * 10**8 : num = 10**8
    sum += num
    kanary = kanary[index_oku+1..-1]
  end
  if (index_man  = kanary.index("万"))
    kanary_temp = kanary[0...index_man]
    num = f_kan2num(kanary_temp)
    (num != 0) ? num = num * 10**4 : num = 10**4
    sum += num
    kanary = kanary[index_man+1 .. -1]
  end
  num = f_kan2num(kanary) if kanary
  sum += num
end

if $0 == __FILE__
  p kan2num(ARGV[0].chomp)
end