开发者

Can't seem to get withDefaultValue to work

I'm doing a code review for Phone Book Mnemonics which Dr. Martin Odersky mentioned over at Skillmatters.

Here's a snippet of what he have:

class Coder(words: List[String]) {

  private val mnemonics = Map(
    '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' -> "JKL",
    '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ")

  /** Invert the mnemonics map to give a map from chars 'A' ... 'Z' to '2' ... '9'*/
  private val charCode: Map[Char, Char] =
    for ((digit, str) <- mnemonics; letter <- str) yield (letter -> digit)


  /** Maps a word to the digit string it can represent */
  private def wordCode(word: String): String = word.toUpperCase map charCode

  /** A map from digit strings to the words that represent them, 
    * e,g. 5282 -> Set(Java, Kata, Lava, ...) */

  private val wordsForNum: Map[String, List[String]] = 
    (words groupBy wordCode) withDefaultValue List()

I've tried to declare everything as function and variable to see how withDefaultValue behave and this is what I got:

scala> val words3 = List("moo", "1111")
words3: List[java.lang.String] = List(moo, 1111)

scala> (words3 groupBy wordCode) withDefaultValue List()
java.util.NoSuchElementException: key not found: $
    at scala.collection.MapLike$class.default(MapLike.scala:224)

In the video he was talking about how if the word is not mapped then we should get an item list (this starts at the 17 min mark in the video). I get an error? I'm using Scala's REPL 2.9.0.1.

Thank you for your time.

Edit:

I'm pretty sure I've generated the val and function def correctly in REPL.

scala> :paste
// Entering paste mode (ctrl-D to finish)

val mnemonics = Map(
    '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' -> "JKL",
    '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ")

// Exiting paste mode, now interpreting.

mnemonics: scala.collection.immutable.Map[Char,java.lang.String] = Map(8 -> TUV, 4 -> GHI, 9 -> WXYZ, 5 -> JKL, 6 -> MNO, 2 -> ABC, 7 -> PQRS, 3 -> DEF)

scala> :paste
// Entering paste mode (ctrl-D to finish)

val charCode: Map[Char, Char] =
    for ((digit, str) <- mnemonics; letter <- str) yield (letter -> digit)

// Exiting paste mode, now interpreting.

charCode: Map[Char,Char] = Map(E -> 3, X -> 9, N -> 6, T -> 8, Y -> 9, J -> 5, U -> 8, F -> 3, A -> 2, M -> 6, I -> 4, G -> 4, V -> 8, Q -> 7, L -> 5, B -> 2, P -> 7, C -> 2, H -> 4, W -> 9, K -> 5, R -> 7, O -> 6, D -> 3, Z -> 9, S -> 7)

def wordCode(word: String): String = word.toUpperCase map charCode

wordCode: (word: String)String

I've also try to defined the whole class in the REPL.

defined class Coder

scala> val words4 = List("hi", "Hello world", "$t@r")
words4: List[java.lang.String] = List(hi, Hello world, $t@r)

scala> var listPhoneNumber = new Coder(words4)
java.util.NoSuchElementException: key not found:  
    at scala.collection.MapLike$class.default(MapLike.scala:224)

scala> val words5 = List("hi","hello","ciao")
words5: List[java.lang.String] = List(hi, hello, ciao)

scala> var listPhoneNumber = new Coder(words5)
listPhoneNumber: Coder = Coder@119c2af

Here's the whole code I'm using:

class Coder(words: List[String]) {



  private val mnemonics = Map(

    '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' 开发者_JS百科-> "JKL",

    '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ")



  /** Invert the mnemonics map to give a map from chars 'A' ... 'Z' to '2' ... '9'*/

  private val charCode: Map[Char, Char] =

    for ((digit, str) <- mnemonics; letter <- str) yield (letter -> digit)



  /** Maps a word to the digit string it can represent */

  private def wordCode(word: String): String = word.toUpperCase map charCode



  /** A map from digit strings to the words that represent them, 

    * e,g. 5282 -> Set(Java, Kata, Lava, ...) */

  private val wordsForNum: Map[String, List[String]] = 

    (words groupBy wordCode) withDefaultValue List()



  /** Return all ways to encode a number as a list of words */

  def encode(number: String): Set[List[String]] =

    if (number.isEmpty)

      Set(List())

    else {

      for {

        splitPoint <- 1 to number.length

        word <- wordsForNum(number take splitPoint)

        rest <- encode(number drop splitPoint)

      } yield word :: rest

    }.toSet



  /** Maps a number to a list of all word phrases that can represent it */

  def translate(number: String): Set[String] = encode(number) map (_ mkString " ")

}

Edit2 - Smaller Snippet:

  val mnemonics = Map(
    '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' -> "JKL",
    '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ")

  val charCode: Map[Char, Char] =
    for ((digit, str) <- mnemonics; letter <- str) yield (letter -> digit)

  def wordCode(word: String): String = word.toUpperCase map charCode

  val words = List("Hello", "1111") // doesn't work

  (words groupBy wordCode) withDefaultValue List()

  val words2 = List("Hello", "Odersky")

  (words2 groupBy wordCode) withDefaultValue List() //works

I've just noticed it seems like the method/function wordCode will not take any character that isn't in the Map. If this is the case does that mean withDefaultValue is useless? If so then I guess the presented code have a little flaw?


Your last notice is correct: as soon as a character is not in the map, the map charCode part of the function wordCode will fail, throwing an exception. Thus you will not reach the wihDefaultPart.

If there is a flaw or not depends on the behaviour you expect when a character is not in the map.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜