Возможно, вы не намерены реализовывать функциональность, но в вашем примере вообще нет необходимости в императивном коде, и return
s и var
s вызывают серьезные проблемы, когда дело доходит до чтения цели кода.
Я бы переписал код примерно так:
sealed trait IP extends Product with Serializable
object IP {
final case class V4(a: Int, b: Int, c: Int, d: Int) extends IP
final case class V6(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int, g: Int, h: Int) extends IP
}
val ipV4 = """(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})""".r
val ipV6 = """([0-9a-fA-F]+):""".r
def validIPAddress(ip: String): Either[String, IP] = {
def parseV4(s: String) = {
val i = Integer.parseInt(s)
if (0 <= i && i <= 255) Right(i) else Left(s"$i is not a valid IPv4 number")
}
def parseV6(s: String) = Integer.parseInt(s, 16)
ip match {
case ipV4(a, b, c, d) =>
for {
a1 <- parseV4(a)
b1 <- parseV4(b)
c1 <- parseV4(c)
d1 <- parseV4(d)
} yield IP.V4(a1, b1, c1, d1)
case ipV6(a, b, c, d, e, f, g, h) =>
// technically speaking this isn't exhausting all possible IPv6 addresses...
// as this regexp would ignore e.g. ::1 or ::
Right(IP.V6(parseV6(a), parseV6(b), parseV6(c), parseV6(d), parseV6(e), parseV6(f), parseV6(g), parseV6(h)))
case _ =>
Left(s"$ip is neither V4 nor V6 format")
}
}
, чтобы упростить отслеживание и отладку ошибок, хотя, как я проверял, эта реализация НЕ действительно соответствует тому, что делает IPv6, как некоторые настройки вокруг использования регулярных выражений будет необходимо. На самом деле, я бы предпочел вообще не использовать свое собственное решение, если бы у меня не было времени написать 15-20 контрольных примеров.
Так что, если нет причин для реализации Ваш собственный валидатор. Я бы поручил эту задачу какой-то библиотеке, которая уже тестировала обработку угловых случаев.
Однако, если вам необходимо выполнить следующие действия:
- не используйте
return
- в Scala он делает что-то отличное от того, что вы думаете - не использует
var
с, если вы не оптимизируете - способ их использования не имеет ничего общего с оптимизацией (isValid
никогда не используется, так зачем его отменять? И другие переменные никогда не изменяются) - не использовать строку для всего, потому что это само по себе создает проблемы (например,
"neither"
и "Neither"
- должен ли абонент использовать .equalsIgnoreCase
в вашем коде для проверки результатов?) - начинать с тестов, написанных на основе спецификации. Пока я переводил ваш код, он не соответствует действительным адресам IPv6. Если бы я пропустил требование, чтобы Iv6 был фиксированного размера, он был бы еще короче:
sealed trait IP extends Product with Serializable
object IP {
final case class V4(a: Int, b: Int, c: Int, d: Int) extends IP
final case class V6(values: List[Int]) extends IP
}
val ipV4 = """(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})""".r
val ipV6 = """([0-9a-fA-F]*)((:[0-9a-fA-F]*){2,})""".r
def validIPAddress(ip: String): Either[String, IP] = {
def parseV4(s: String) = {
val i = Integer.parseInt(s)
if (0 <= i && i <= 255) Right(i) else Left(s"$i is not a valid IPv4 number")
}
def parseV6(s: String) = if (s.isEmpty) 0 else Integer.parseInt(s, 16)
ip match {
case ipV4(a, b, c, d) =>
for {
a1 <- parseV4(a)
b1 <- parseV4(b)
c1 <- parseV4(c)
d1 <- parseV4(d)
} yield IP.V4(a1, b1, c1, d1)
case ipV6(head, tail, _) =>
val segments = head +: tail.substring(1).split(':')
Right(IP.V6(segments.map(parseV6).toList))
case _ =>
Left(s"$ip is neither V4 nor V6 format")
}
}
, который обрабатывал более правильные случаи, но все еще далек от готовности. Поэтому, если вы можете - избегайте делать это самостоятельно и используйте библиотеку.