3文字読む関数と1桁読む関数
def eat_repeat(chrs, c,max):
count = 0
while len(chrs) > 0:
cc = chrs.pop()
if cc != c:
chrs.append(cc)
break
count += 1
if count > max:
return -1
return count
def eat_digit(chrs, one, five, ten,
mul):
if len(chrs) == 0:
print('empty')
return -1
c = chrs.pop()
if c == one:
v = eat_repeat(chrs, one, 2)
if v > 0:
return mul * (v + 1)
elif v < 0:
return v
if len(chrs) == 0:
return mul
c2 = chrs.pop()
if c2 == ten:
return mul * 9
elif c2 == five:
return mul * 4
else:
chrs.append(c2)
return mul
elif c == five:
v = eat_repeat(chrs, one, 3)
if v >= 0:
return mul * (v + 5)
else:
return v
else:
chrs.append(c)
return 0
結果クラスと連続文字パース
sealed case classParseResult(chrs: Seq[Char], num: Int) {
def +(add: Int) = ParseResult(chrs, num + add)
}
private def parseRepeated(chrs: Seq[Char], chr: Char, mul: Int):
Option[ParseResult] = {
val next = chrs.indexWhere(_ != chr)
val drop = if (next >= 0) next else chrs.length
if (drop <= 3) {
Some(ParseResult(chrs.drop(drop), drop * mul))
} else {
None
}
}
15.
各桁のパース
private def parseThousand(chrs:Seq[Char]): Option[ParseResult] = parseRepeated(chrs,
'M', 1000)
private def parseDigit(chrs: Seq[Char], chr1: Char, chr5: Char, chr10: Char, mul:
Int): Option[ParseResult] =
chrs.headOption match {
case Some(c) if c == chr1 =>
chrs.tail.headOption match {
case Some(cc) if cc == chr10 =>
Some(ParseResult(chrs.drop(2), mul * 9))
case Some(cc) if cc == chr5 =>
Some(ParseResult(chrs.drop(2), mul * 4))
case Some(cc) if cc == chr1 =>
parseRepeated(chrs, chr1, mul)
case _ =>
Some(ParseResult(chrs.tail, mul))
}
case Some(c) if c == chr5 =>
parseRepeated(chrs.tail, chr1, mul).map(_ + mul * 5)
case _ =>
Some(ParseResult(chrs, 0))
16.
統合
override def parseRoman(str:String): Int = (for {
t <- parseThousand(str)
h <- parseDigit(t.chrs, 'C', 'D', 'M', 100)
e <- parseDigit(h.chrs, 'X', 'L', 'C', 10)
o <- parseDigit(e.chrs, 'I', 'V', 'X', 1)
} yield {
val value = if (o.chrs.isEmpty) t.num + h.num + e.num + o.num
else -1
if (value > 0) value else -1
}).getOrElse(-1)
数値に変換=足し算
def parse(str):
m =r.match(str)
if m is None:
return -1
else:
return parse_thousand(m.group(1)) +
parse_hundred(m.group(2)) +
parse_ten(m.group(3)) +
parse_one(m.group(4))
RegexParsersでべた書き
lazy val hundred9:Parser[Int] = "CM" ^^ (_ => 900)
lazy val hundred5: Parser[Int] = "DC{0,3}".r ^^ (_.length() * 100 -
100 + 500)
lazy val hundred4: Parser[Int] = "CD" ^^ (_ => 400)
lazy val hundred1: Parser[Int] = "C{0,3}".r ^^ (_.length * 100)
lazy val hundred: Parser[Int] = hundred9 | hundred5 | hundred4 |
hundred1
lazy val romanNumerals: Parser[Int] = thousand ~! hundred ~! ten ~!
one ^^ {
case t ~ h ~ e ~ o => t + h + e + o
}
def parseRoman(str:String): Int = parseAll(romanNumerals, str) match
{
case Success(num, next) => if (num > 0) num else -1
case NoSuccess(errorMessage, next) => -1
}
RegexParsersでパーサー生成(2)
lazy val romanNumerals:Parser[Int] =
createParser('M', None, None, 1000) ~!
createParser('C', Some('D'), Some('M'), 100) ~!
createParser('X', Some('L'), Some('C'), 10) ~!
createParser('I', Some('V'), Some('X'), 1) ^^ {
case t ~ h ~ e ~ o => t + h + e + o
}
def parseRoman(str:String): Int = parseAll(romanNumerals, str)
match {
case Success(num, next) => if (num > 0) num else -1
case NoSuccess(errorMessage, next) => -1
}