Scala - 使用更高级别的方法缩短代码段

时间:2017-12-01 23:58:16

标签: scala code-cleanup

如何使用更高级别的方法缩短以下代码段(toText方法),例如maptakeWhilefilter等:

/** Returns the text that is produced by this keypad from the given multi-tap input. 
  * For instance, the input `"33 3338881330000"` produces `"HIYAH!"`. The given string
  * is assumed to consist of digits and spaces only.
  */
def toText(keysPressed: String): String = {
 val pieces = keysPressed.split(" ")
 var result = ""                        // gatherer
 for (currentPiece <- pieces) {         // most-recent holder
   var remaining = currentPiece         // gatherer 
   while (remaining.nonEmpty) {                    
     val copyCount = countInitialCopies(remaining) // temporary
     result += charFor(remaining(0), copyCount)
     remaining = remaining.drop(copyCount)
   }
 }
 result
} 

其中:

/** Returns the character produced by pressing the given number key (from '0' to '9')
  * the given number of times on this keypad. If a key is pressed more times than there
  * are characters assigned to the key, the result "wraps around".
  */
def charFor(keyPressed: Char, timesPressed: Int): Char = {
  val charactersForKeys = Vector(" .,!?", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ", "ÅÄÖ")
  val key = charactersForKeys(keyPressed.asDigit)
  key((timesPressed-1) % key.length)
}

/** Determines the first letter of the given string, and returns the number of times 
  * the letter occurs consecutively at the beginning of the string. The given
  * string must have at least one character.
  */
def countInitialCopies(str: String): Int = str.takeWhile(_ == str(0)).length

我尝试做以下事情,但它没有走得那么远:

def toText(keysPressed: String): String = keysPressed.split(" ").foldLeft("")(charFor(_(0), countInitialCopies(_)))

2 个答案:

答案 0 :(得分:1)

不确定它是否真的更短但是这里有一个:

def toText(keysPressed: String): String = {
  def split(seq: Seq[Char]): Stream[Seq[Char]] = {
    if (seq.isEmpty)
      Stream.empty
    else {
      val lr = seq.span(ch => ch == seq.head)
      Stream.cons(lr._1, split(lr._2))
    }
  }

  split(keysPressed)
    .filter(s => s.head.isDigit) // filter out spaces between different series of the same digits
    .map(s => charFor(s.head, s.length))
    .mkString("")
}

此代码背后的理念是:

  1. 首先将keysPressed(被Seq[Char]视为scala.collection.immutable.StringOps视为隐含的Stream[Seq[Char]])分成countInitialCopies,方式与cons类似(请注意{ {1}}第二个参数是Seq的其余部分的#34;递归&#34;(实际延迟)调用!)
  2. 然后过滤出Seq[Char]来自显式组分隔的空格,
  3. 然后map使用您的Stream[Seq[Char]]
  4. 过滤了charFor
  5. 最终将Stream的结果累积到String

答案 1 :(得分:1)

这是解决问题的方法略有不同。使用takeWhile()但不多。

val charsForKeys = Vector(" .,!?", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ")

def toText(keysPressed: String): String = {
  if (keysPressed.isEmpty) ""
  else {
    val kpHead = keysPressed.head
    val kpStr  = keysPressed.takeWhile(_ == kpHead)
    val kpLen  = kpStr.length
    if (kpHead.isDigit)
      charsForKeys(kpHead.asDigit)(kpLen-1) + toText(keysPressed.drop(kpLen))
    else
      toText(keysPressed.drop(kpLen))  //not a digit, skip these chars
  }
}

toText("33 3338881330000")  //res0: String = HIYAH!

2,更短,尝试。使用foldRight()collect()

def toText(keysPressed: String): String = {
  keysPressed.foldRight(List[String]()){
    case (c, s::ss) => if (c == s.head) c+s :: ss
                       else c.toString :: s :: ss
    case (c, Nil)   => List(c.toString)
  }.collect{
    case s if s.head.isDigit => charsForKeys(s.head.asDigit)(s.length-1)
  }.mkString
}