Используйте моноиды для вычисления частотной карты слов
Я изучаю книгу функционального программирования в Scala. глава 10, упражнение 20, для реализации следующего метода:
def frequencyMap(strings: IndexedSeq[String]): Map[String, Int]
честно говоря, у меня нет решения, поэтому я проверил ответ из GIT:
def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] =
new Monoid[Map[K, V]] {
def zero = Map()
def op(a: Map[K, V], b: Map[K, V]) =
a.map {
case (k, v) => (k, V.op(v, b.get(k) getOrElse V.zero))
}
}
def bag[A](as: IndexedSeq[A]): Map[A, Int] =
foldMapV(as, mapMergeMonoid[A, Int](intAddition))((a: A) => Map(a -> 1))
def frequencyMap(strings: IndexedSeq[String]): Map[String, Int] = bag(strings)
Но когда я попытался запустить тест, я получил неправильный ответ:
frequencyMap(Vector("a rose", "is a", "rose is", "a rose"))
ответ напечатан:
Map(a rose -> 1)
ожидаемый результат:
Map(a -> 3, rose -> 3, is -> 2)
Я не могу понять, где не так с реализацией. Может ли кто-нибудь объяснить это для меня? Благодарю.
Отредактируйте после правильного ответа и обновите с правильной реализацией
def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] =
new Monoid[Map[K, V]] {
def zero = Map()
def op(a: Map[K, V], b: Map[K, V]) = {
// (a.map {
// case (k, v) => (k, V.op(v, b.get(k) getOrElse V.zero))
//
val c = for {
(ka, va) <- a
(kb, vb) <- b
if (ka == kb)
} yield (ka -> V.op(va, vb))
a ++ b ++ c
}
}
def bag[A](as: IndexedSeq[A]): Map[A, Int] =
foldMapV(as, mapMergeMonoid[A, Int](intAddition))((a: A) => Map(a -> 1))
def frequencyMap(strings: IndexedSeq[String]): Map[String, Int] = bag(strings.map(_.split("\\s+")).flatten)
1 ответ
У меня нет книги, но, следуя приведенному здесь коду, я могу указать на несколько вещей, о которых вы должны подумать (основываясь на некоторых println() в сеансе REPL):
Как указал SRI, у вас есть список фраз, а не слов. Функции на github ничего не делают для разделения групп слов для вас, поэтому в лучшем случае ваш текущий входной вектор может получить:
Map(a rose -> 2, is a -> 1, rose is -> 1)
Вы можете создать список слов, выполнив следующие действия для вашего вектора:
Vector("a rose", "is a", "rose is", "a rose") map( _.split( " " ).toSeq ) flatten
mapMergeMonoid
Функция появляется только для добавления значений k и v вместе, когда клавиши (k) совпадают. Значение несортированного списка приведет к большому количествуMap(string -> 1)
,Вы можете отсортировать Вектор слов, внеся следующие изменения в Вектор:
(Vector("a rose", "is a", "rose is", "a rose") map( _.split( " " ).toSeq ) flatten) sortWith(_.compareTo(_) < 0)
В то время как
foldMapV
вычленяет все фразы или слова из вектора вMap[String, Int]
, он только возвращает наиболее левое желаемое слияние для отсортированного IndexedSeq. С отсортированным вектором слов, как я указал, я получил результатMap(a -> 3)
от вашей реализацииfrequencyMap
, Я был бы склонен использовать что-то кромеfoldMapV
хотя или изменить его, чтобы сделатьfrequencyMap
Работа. Бит, который мне кажется отсутствующим, - это скопление результатов функции, которую он применяет в одной большой карте. Я также попробовал бы более "стандартную" головную / хвостовую рекурсию, чем вызовы splitAt() (поскольку я не уверен, чтоfoldMapV
обрабатывалis
а такжеrose
отлично).