はじめに
業務で、漢数字を含む文字列を数値に変換する処理が必要になりました。昔Javaで実装した処理はScala、Pythonをかじり始めた今見返すと残念でしかなく、チョロっと調べた範囲でもあまりScalaっぽいサンプルも見つからず。
なので、foldRightの練習も兼ねて関数を作成してみました。ScalaやPythonは半年くらいの片手間実装経験しかないので、そもそも人のコードも自分のコードもScalaっぽい、とか言える程の理解も腕前もありませんが。。。
結論
先に結論ですが、foldRight
便利!
あとScalaもやっぱり便利!
昔々Javaで書いたロジックに比べてとても簡潔になりました。
Scalaっぽい気がする!
文字定義
数値文字、桁文字を以下の通りマップで定義します。京
以上の桁はLongを超えてしまうのでとりあえず桁はここまでです。BigDecimalって何桁いけるのかわかりませんが、仮にScalaの数値桁が対応しており、恒河沙
みたいな複数文字で表現される桁文字列にも対応したい場合は、マップのキーを桁文字(Char)ではなく、桁文字列(String)にすればいいのかな?まぁ実用上問題ないからこのままでいいかな、と。
// 数値文字 -> 値マップ
private val numericCharacterMap = Map(
'0' -> 0,
'0' -> 0,
'零' -> 0,
'〇' -> 0,
'1' -> 1,
'1' -> 1,
'一' -> 1,
'壱' -> 1,
'2' -> 2,
'2' -> 2,
'二' -> 2,
'弐' -> 2,
'3' -> 3,
'3' -> 3,
'三' -> 3,
'参' -> 3,
'4' -> 4,
'4' -> 4,
'四' -> 4,
'5' -> 5,
'5' -> 5,
'五' -> 5,
'6' -> 6,
'6' -> 6,
'六' -> 6,
'7' -> 7,
'7' -> 7,
'七' -> 7,
'8' -> 8,
'8' -> 8,
'八' -> 8,
'9' -> 9,
'9' -> 9,
'九' -> 9,
)
// 桁文字 -> 桁マップ
private val digitMap = Map(
'十' -> 1,
'百' -> 2,
'千' -> 3,
'万' -> 4,
'萬' -> 4,
'億' -> 8,
'兆' -> 12,
'京' -> 16,
)
文字列 -> 数値変換関数
こんな感じになりました。
def convertFromKanjiToNumber(input: String): Long = {
// 定義に無い文字は捨てる(桁区切り文字等)
var tmp = input.filter(x => numericCharacterMap.contains(x) || digitMap.contains(x))
// 桁文字(十、万等)の前に数値文字が無い場合”1”を追加(”百十”を”1百1十”に)
tmp = tmp.head + tmp
tmp = tmp.sliding(2).map(x => {
if (digitMap.contains(x(0)) && digitMap.contains(x(1)) && (digitMap(x(0)) >= digitMap(x(1))))
"1" + x(1)
else
x(1)
}).mkString("")
// foldRightで下位の文字から数値に変換して足し込む
// 初期値は値、桁(十百千の桁)、桁(万以上の桁)のtuple)
val result = tmp.foldRight((0L, 0, 0)){ (x, current) =>
// 桁文字の場合、桁の繰り上げ
if (digitMap.contains(x))
(current._1, if (digitMap(x) > 3) 0 else digitMap(x), if (digitMap(x) > 3) digitMap(x) else current._3)
// 数値文字の場合、桁を適用して足し込み
else
(current._1 + numericCharacterMap(x) * Math.pow(10, current._2 + current._3).toLong, current._2 + 1, current._3)
}
result._1
}
テストデータ
以下はとりあえず正しく変換できました
val testDataList = List(
("一", 1),
("十四", 14),
("二十三", 23),
("四十", 40),
("千六", 1006),
("一二三", 123),
("千十", 1010),
("5千二百", 5200),
("5千万", 5000 * 10000),
("百十萬", 100 * 10000 + 10 * 10000),
("十二万", 120 * 1000),
("7千五十億百七五", 7000L * 10000L * 10000L + 50L * 10000L * 10000L + 175L)
)