Skip to content

Commit

Permalink
Implement 5th assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-myltsev committed Dec 14, 2013
1 parent 81b3d88 commit 8ef00d7
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 30 deletions.
73 changes: 56 additions & 17 deletions a5-forcomp/src/main/scala/forcomp/Anagrams.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ object Anagrams {
* how often the character appears.
* This list is sorted alphabetically w.r.t. to the character in each pair.
* All characters in the occurrence list are lowercase.
*
*
* Any list of pairs of lowercase characters and their frequency which is not sorted
* is **not** an occurrence list.
*
*
* Note: If the frequency of some character is zero, then that character should not be
* in the list.
*/
Expand All @@ -29,19 +29,25 @@ object Anagrams {
val dictionary: List[Word] = loadDictionary

/** Converts the word into its character occurence list.
*
*
* Note: the uppercase and lowercase version of the character are treated as the
* same character, and are represented as a lowercase character in the occurrence list.
*/
def wordOccurrences(w: Word): Occurrences = ???
def wordOccurrences(w: Word): Occurrences =
w.toLowerCase
.groupBy(x => x)
.map { case (c, s) => (c, s.length) }
.toList
.sortBy(x => x._1)

/** Converts a sentence into its character occurrence list. */
def sentenceOccurrences(s: Sentence): Occurrences = ???
def sentenceOccurrences(s: Sentence): Occurrences =
wordOccurrences(s.mkString(""))

/** The `dictionaryByOccurrences` is a `Map` from different occurrences to a sequence of all
* the words that have that occurrence count.
* This map serves as an easy way to obtain all the anagrams of a word given its occurrence list.
*
*
* For example, the word "eat" has the following character occurrence list:
*
* `List(('a', 1), ('e', 1), ('t', 1))`
Expand All @@ -53,16 +59,18 @@ object Anagrams {
* List(('a', 1), ('e', 1), ('t', 1)) -> Seq("ate", "eat", "tea")
*
*/
lazy val dictionaryByOccurrences: Map[Occurrences, List[Word]] = ???
lazy val dictionaryByOccurrences: Map[Occurrences, List[Word]] =
dictionary.groupBy(x => wordOccurrences(x)).withDefaultValue(List())

/** Returns all the anagrams of a given word. */
def wordAnagrams(word: Word): List[Word] = ???
def wordAnagrams(word: Word): List[Word] =
dictionaryByOccurrences(wordOccurrences(word))

/** Returns the list of all subsets of the occurrence list.
* This includes the occurrence itself, i.e. `List(('k', 1), ('o', 1))`
* is a subset of `List(('k', 1), ('o', 1))`.
* It also include the empty subset `List()`.
*
*
* Example: the subsets of the occurrence list `List(('a', 2), ('b', 2))` are:
*
* List(
Expand All @@ -80,10 +88,17 @@ object Anagrams {
* Note that the order of the occurrence list subsets does not matter -- the subsets
* in the example above could have been displayed in some other order.
*/
def combinations(occurrences: Occurrences): List[Occurrences] = ???
def combinations(occurrences: Occurrences): List[Occurrences] = occurrences match {
case Nil => List(occurrences)
case (ch, i) :: rest =>
(for {
x <- (1 to i).toList map { case x => (ch, x) }
r <- combinations(rest)
} yield x :: r) ++ combinations(rest)
}

/** Subtracts occurrence list `y` from occurrence list `x`.
*
*
* The precondition is that the occurrence list `y` is a subset of
* the occurrence list `x` -- any character appearing in `y` must
* appear in `x`, and its frequency in `y` must be smaller or equal
Expand All @@ -92,10 +107,20 @@ object Anagrams {
* Note: the resulting value is an occurrence - meaning it is sorted
* and has no zero-entries.
*/
def subtract(x: Occurrences, y: Occurrences): Occurrences = ???
def subtract(x: Occurrences, y: Occurrences): Occurrences = {
val res = y.toMap.foldLeft(x.toMap) {
case (xi, (ch, freq)) =>
{
val i = xi(ch) - freq
if (i == 0) (xi - ch)
else xi.updated(ch, i)
}
}
res.toList.sortBy(_._1)
}

/** Returns a list of all anagram sentences of the given sentence.
*
*
* An anagram of a sentence is formed by taking the occurrences of all the characters of
* all the words in the sentence, and producing all possible combinations of words with those characters,
* such that the words have to be from the dictionary.
Expand All @@ -106,7 +131,7 @@ object Anagrams {
* Also, two sentences with the same words but in a different order are considered two different anagrams.
* For example, sentences `List("You", "olive")` and `List("olive", "you")` are different anagrams of
* `List("I", "love", "you")`.
*
*
* Here is a full example of a sentence `List("Yes", "man")` and its anagrams for our dictionary:
*
* List(
Expand All @@ -128,12 +153,26 @@ object Anagrams {
*
* The different sentences do not have to be output in the order shown above - any order is fine as long as
* all the anagrams are there. Every returned word has to exist in the dictionary.
*
*
* Note: in case that the words of the sentence are in the dictionary, then the sentence is the anagram of itself,
* so it has to be returned in this list.
*
* Note: There is only one anagram of an empty sentence.
*/
def sentenceAnagrams(sentence: Sentence): List[Sentence] = ???

def sentenceAnagrams(sentence: Sentence): List[Sentence] = {
if (sentence.isEmpty) List(sentence)
else {
def occurrencesAnagrams(occurrence: Occurrences): List[Sentence] = {
if (occurrence.isEmpty) List(Nil)
else {
for (
comb <- combinations(occurrence);
word <- dictionaryByOccurrences(comb);
restSentence <- occurrencesAnagrams(subtract(occurrence, comb))
) yield word :: restSentence
}
}
occurrencesAnagrams(sentenceOccurrences(sentence))
}
}
}
48 changes: 35 additions & 13 deletions a5-forcomp/src/test/scala/forcomp/AnagramsSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,25 @@ class AnagramsSuite extends FunSuite {
assert(wordOccurrences("Robert") === List(('b', 1), ('e', 1), ('o', 1), ('r', 2), ('t', 1)))
}


test("wordOccurrences: raAaRrRaarR") {
assert(wordOccurrences("raAaRrRaarR") === List(('a', 5), ('r', 6)))
}

test("sentenceOccurrences: abcd e") {
assert(sentenceOccurrences(List("abcd", "e")) === List(('a', 1), ('b', 1), ('c', 1), ('d', 1), ('e', 1)))
}


test("sentenceOccurrences: Robert raAaRrRaar") {
assert(sentenceOccurrences(List("Robert", "raAaRrRaarR")) === List(('a', 5), ('b', 1), ('e', 1), ('o', 1), ('r', 8), ('t', 1)))
}

test("dictionaryByOccurrences.get: eat") {
assert(dictionaryByOccurrences.get(List(('a', 1), ('e', 1), ('t', 1))).map(_.toSet) === Some(Set("ate", "eat", "tea")))
}


test("dictionaryByOccurrences.get: empty list if not exist") {
assert(dictionaryByOccurrences(List(('b', 1))) === List())
}

test("word anagrams: married") {
assert(wordAnagrams("married").toSet === Set("married", "admirer"))
Expand All @@ -40,7 +46,9 @@ class AnagramsSuite extends FunSuite {
assert(wordAnagrams("player").toSet === Set("parley", "pearly", "player", "replay"))
}


test("word anagrams: empty list if no exist") {
assert(wordAnagrams("sashka") === List())
}

test("subtract: lard - r") {
val lard = List(('a', 1), ('d', 1), ('l', 1), ('r', 1))
Expand All @@ -49,8 +57,6 @@ class AnagramsSuite extends FunSuite {
assert(subtract(lard, r) === lad)
}



test("combinations: []") {
assert(combinations(Nil) === List(Nil))
}
Expand All @@ -66,18 +72,35 @@ class AnagramsSuite extends FunSuite {
List(('a', 2), ('b', 1)),
List(('b', 2)),
List(('a', 1), ('b', 2)),
List(('a', 2), ('b', 2))
)
List(('a', 2), ('b', 2)))
assert(combinations(abba).toSet === abbacomb.toSet)
}



test("sentence anagrams: []") {
val sentence = List()
assert(sentenceAnagrams(sentence) === List(Nil))
}

test("sentence anagrams: Yes man") {
val sentence = List("Yes", "man")
val anas = List(
List("en", "as", "my"),
List("en", "my", "as"),
List("man", "yes"),
List("men", "say"),
List("as", "en", "my"),
List("as", "my", "en"),
List("sane", "my"),
List("Sean", "my"),
List("my", "en", "as"),
List("my", "as", "en"),
List("my", "sane"),
List("my", "Sean"),
List("say", "men"),
List("yes", "man"))
assert(sentenceAnagrams(sentence).toSet === anas.toSet)
}

test("sentence anagrams: Linux rulez") {
val sentence = List("Linux", "rulez")
val anas = List(
Expand All @@ -100,9 +123,8 @@ class AnagramsSuite extends FunSuite {
List("Uzi", "Rex", "null"),
List("Zulu", "nil", "Rex"),
List("rulez", "Linux"),
List("Linux", "rulez")
)
List("Linux", "rulez"))
assert(sentenceAnagrams(sentence).toSet === anas.toSet)
}
}

}

0 comments on commit 8ef00d7

Please sign in to comment.