Phone Mnemonics and Dr. Martin Odersky keynote at Skillsmatter

45 views
Skip to first unread message

Anthony Doan

unread,
Aug 23, 2011, 1:39:22 PM8/23/11
to scala-user
http://stackoverflow.com/questions/7156653/cant-seem-to-get-withdefaultvalue-to-work

The example given at the presentation doesn't behavior exactly what
have been described.

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()


The above code does not work with characters that doesn't exist in the
map, charCode. It throws an exception.

But in the presentation (http://skillsmatter.com/podcast/scala/
keynote-2067 @ 17 min), it should behave differently. It should take
whatever that is not in the map and return a default value of empty
list (withDefaultValue List()). But! It prematurely fail at the method/
function wordCode.

My question now is, how would I get the behavior as described in the
presentation? I mean I probably can get it but is there an elegant way
of getting it? I thought about doing a (word.toUpperCase map
charCode) withDefaultvalue List() but that doesn't work. Note: I'm
still learning Scala.

Thank you for your time!

Dave

unread,
Aug 23, 2011, 2:44:31 PM8/23/11
to scala-user
This code should work:
https://gist.github.com/1158801

I have added these lines:
println(coder.translate("4355696753"))
println(coder.translate("8447472665"))
println(coder.translate("1111"))
println(coder.translate("!@#$"))

Change:
val dict = io.Source.fromFile("words.txt")
.getLines.filter(_.length > 1).filter(_.matches("[a-zA-Z]
+")).toList

so that it points to a textfile in the current directory

Create a file with words that encodes to the phone numbers

words.txt
=========
Hello
world
This
is
cool

Your output should be:
Set(Scala rocks)
Set(Hello world)
Set(This is cool)
Set()
Set()

Add some words or make a phrase of words add that to the file, encode
it to phone number and add a line
println(coder.translate("<phone number>")
Then you see that it decodes it to the phrase.

Dave

unread,
Aug 23, 2011, 3:30:18 PM8/23/11
to scala-user
If you mean that a word may not contain numbers then it is by design
because the mnemonics are only the letters of the alphabet a-z or
uppercase. If you want the mnemonics can also contain digits then you
should add 0-9 to the value of the map
val mnemonics = Map('0' -> "0", '1' -> "1",
'2' -> "2ABC", '3' -> "3DEF", '4' -> "4GHI", '5' -> "5JKL",
'6' -> "6MNO", '7' -> "7PQRS", '8' -> "8TUV", '9' -> "9WXYZ")

and modify the regex in line
val dict = io.Source.fromFile("words.txt")
.getLines.filter(_.length > 1).filter(_.matches("[a-zA-
Z0-9]+")).toList

If you add:
println(coder.translate("74992123"))

words.txt
=========
Hello
world
This
is
cool
pizza
123



then you get:

Set(Scala rocks)
Set(Hello world)
Set(This is cool)
Set()
Set()
Set(pizza 123)

Anthony Doan

unread,
Aug 23, 2011, 3:33:52 PM8/23/11
to scala-user
Note: A missing number should map to the empty set, e.g. "1111" ->
List()
(words groupBy wordCode) withDefaultValue List()

Can you clarify this comment and statement?

I don't see when or how withDefaultValue List() is going to go into
effect.

My assumption, probably wrong, is that if one of the word contains
characters that isn't in the Map, it default to List() ?

var example = new coder(List("$t@r","11111"))

I am confuse with
withDefaultValue List() ?

Thank you.

Anthony Doan

unread,
Aug 23, 2011, 3:35:28 PM8/23/11
to scala-user
Ah, but I thought the withDefaultValue was suppose to take care of it?
I thought that was the reasoning in the presentation?

Dave

unread,
Aug 23, 2011, 7:14:52 PM8/23/11
to scala-user
"Note: A missing number should map to the empty set, e.g. "1111" ->
List()"
means:
"withDefaultValue List()" is triggered and returns List() called for
instance with a part of the phone number "1111" or "7" etc but there
is no match otherwise you'll get a NoSuchElementException.
wordsForNum is called in def encode with a parts of the phonenumber to
try if there is a match (even if words is empty)

def encode can be improved with checking words.isEmpty because it
makes no sense to encode a phonenumber if there are no words to encode
to.

def encode(number: String): Set[List[String]] =
if (number.isEmpty || words.isEmpty) Set(List())
else {
for {
split <- 1 to number.length
word <- wordsForNum(number take split)
rest <- encode(number drop split)
} yield word :: rest
}.toSet


you can test withDefaultValue with
e.g.

scala> ((words groupBy wordCode) withDefaultValue List("Triggered"))
("1111")
res24: List[java.lang.String] = List(Triggered)


def wordCode should also be modified because if the list of words
contains words with characters that are not in the mnemonic map
values.
you'll get a NoSuchElementException

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

Damian H

unread,
Aug 24, 2011, 8:07:57 AM8/24/11
to scala...@googlegroups.com
Hopefully this helps:

scala> words
res69: List[java.lang.String] = List(java, lava, blah, ja)

scala> (words groupBy wordCode)
res70: scala.collection.immutable.Map[String,List[java.lang.String]] = Map((2524,List(blah)), (52,List(ja)), (5282,List(java,lava)))

scala> (words groupBy wordCode)("5282")
res71: List[java.lang.String] = List(java, lava)

scala> (words groupBy wordCode)("1111")
java.util.NoSuchElementException: key not found: 1111
        at scala.collection.MapLike$class.default(MapLike.scala:223)
        at scala.collection.immutable.Map$Map3.default(Map.scala:132)
        at scala.collection.MapLike$class.apply(MapLike.scala:134)
        at scala.collection.immutable.Map$Map3.apply(Map.scala:132)
        at .<init>(<console>:16)
        at .<clinit>(<console>)
        at RequestResult$.<init>(<console>:9)
        at RequestResult$.<clinit>(<console>)
        at RequestResult$scala_repl_result(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$ap...
scala> ((words groupBy wordCode) withDefaultValue(List())) ("1111")
res74: List[java.lang.String] = List()

It looks to me like you might be mixing up the list of words in the dictionary with the input numerical string (e.g. "1111"). When you say: 

>> My assumption, probably wrong, is that if one of the word contains 
>> characters that isn't in the Map, it default to List() ? 

I think you ought to be saying:
Either
>> My assumption,... is that if  input numerical string doesn't map to any of the dictionary words it default to List() ? 
Or, something like
>> If any of the dictionary words contains a character that is not in the mnemonic map, then it should be ignored by wordsForNum.

'withDefaultValue List()' deals with the former; the latter isn't dealt with.

Damian.

Reply all
Reply to author
Forward
0 new messages