2

我是 Scala 新手。我尝试了土耳其公民号码验证算法。

如何实现和优化这个 scala 代码?

您可以通过此链接找到我的 java 版本https://gist.github.com/hasanozgan/5601623

trait TurkishCitizenshipNumberValidator {

  private def odd(tckn: String): Int = {
    tckn.zipWithIndex.foldLeft(0) {
      (total, x) =>
        x match {
          case i if ((i._2 % 2 == 0 && i._2 < 10)) => ((i._1.asDigit) + total)
          case _ => total
        }
    }
  }

  private def even(tckn: String): Int = {
    tckn.zipWithIndex.foldLeft(0) {
      (total, x) =>
        x match {
          case i if ((i._2 % 2 == 1) && i._2 < 9) => ((i._1.asDigit) + total)
          case _ => total
        }
    }
  }

  private def total(tckn: String): Int = {
    tckn.zipWithIndex.foldLeft(0) {
      (total, x) =>
        x match {
          case i if (i._2 < 10) => ((i._1.asDigit) + total)
          case _ => total
        }
    }
  }

  def turkishCitizenshipNumberValidator(t: String): Boolean = {
    val digit10 = total(t) % 10
    val digit9 = ((odd(t) * 7) - even(t)) % 10

    ((t(9).asDigit == digit9) && t(10).asDigit == digit10)
  }
}

object test extends TurkishCitizenshipNumberValidator {
  // http://tckimliknouretici.appspot.com/
    turkishCitizenshipNumberValidator("29419391592")
                                                  //> res0: Boolean = true
}
4

4 回答 4

2

如果你想要它既紧凑又清晰,并且需要基本的输入验证(正确的长度,东西是数字),我会

def turkishCitizenshipNumberValidator(t: String): Boolean = {
  if (t.length != 11 || !t.forall(_.isDigit)) false
  else {
    val n = t.map(_.asDigit)
    val evens = n.grouped(2).take(5).map(_(0)).sum
    val odds = n.grouped(2).take(4).map(_(1)).sum
    n(10) == (n.take(10).sum % 10) && n(9) == ((odds*7 - evens) % 10)
  }
}

这里的键grouped用于将字符串以奇偶对分开,并在开始时将数字映射到数字,因此使用它们不会太令人头疼。


编辑:如果你想混淆你的代码,试试这个!

def tCNV(t: String) = t.map(_.asDigit).foldLeft(Seq(0,0,0)){ (v,n) => v(2) match {
  case 10 => Seq(v(0)-n,v(1),0); case 9 => Seq(v(0)+n,v(1)-n,10)
  case i => Seq(v(0)+n, v(1)+n*(7*(i%2)+(i%2-1)), i+1)
}}.take(2).map(_%10).forall(_ == 0)
于 2013-05-17T23:15:04.840 回答
1

好吧,你有三倍相同的身体,但有细微的差别。因此,您应该将该主体分解为一种辅助方法,并带有一个额外的函数参数来测试索引:

private def check(tckn: String)(pred: Int => Boolean): Int = {
  tckn.zipWithIndex.foldLeft(0) {
    (total, x) =>
      x match {
        case i if pred(i._2) => ((i._1.asDigit) + total)
        case _ => total
      }
  }
}

private def odd(tckn: String): Int = check(tckn)(i => i % 2 == 0 && i < 10)

等等


其次,您可以通过提取字符和索引的元组来稍微简化check代码,如果您只有一个守卫,则不需要模式匹配,就像一个简单的if那样(不过更多的是品味问题)。

private def check(tckn: String)(pred: Int => Boolean): Int =
  tckn.zipWithIndex.foldLeft(0) { case (total, (char, idx)) =>
    if (pred(idx)) char.asDigit + total else total
  }
于 2013-05-17T20:24:17.463 回答
1

这个怎么样?

def turkishCitizenshipNumberValidator(t: String): Boolean = {
  val digits = t.init.map(_.asDigit)
  val digit10 = digits.sum % 10
  val (odd, even) = digits.zipWithIndex.partition(_._2 % 2 == 0)
  val digit9 = ((odd.map(_._1).sum * 7) - even.init.map(_._1).sum) % 10

  ((t(9).asDigit == digit9) && t(10).asDigit == digit10)
}
于 2013-05-17T21:52:33.363 回答
0

对于需要解构然后处理字符串的此类问题,有时将模式压缩到字符串然后使用通常的收集方法进行处理会更容易。

在这种特定情况下,该模式是一个简单的奇偶模式,因此将公民号码压缩成一串交替的字符,一二,是开始的地方,就像这样......

def turkishCitizenshipNumberValidator(数字:字符串)= {

  val pattern = Stream.continually("12".toStream).flatten // 奇偶模式

  val d = 数字。
          过滤器(_.isDigit)。// 过滤掉非数字
          地图(_.asDigit)。// 将 Char 转换为 Int
          zip(pattern) // 压缩到一个二的重复字符串,用于奇数和偶数

  val 赔率 = d.take(9).filter( _._2 == '1' ).map( _._1 ).sum
  val evens = d.take(8).filter(_._2 == '2').map(_._1).sum
  val 总计 = d.take(10).map( _._1 ).sum

  d.size == 11 && (odds * 7 - evens) % 10 == d(9)._1 && total % 10 == d(10)._1
}

turkishCitizenshipNumberValidator("29419391592")
于 2013-05-18T01:04:25.093 回答