使用monoids计算单词的频率图

时间:2014-04-07 03:25:33

标签: scala functional-programming

我正在学习本书 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 个答案:

答案 0 :(得分:1)

我没有这本书,但是按照here的代码,我可以指出一些事情供您考虑(基于REPL会话中的一些println()):

  • 正如S.R.I指出的那样,你有一个短语列表,而不是单词。 github上的函数没有做任何事情来为你分隔单词组,所以最好,你当前的输入Vector可以得到:

    Map(a rose -> 2, is a -> 1, rose is -> 1)
    

    您可以通过对Vector执行以下操作来创建单词列表:

    Vector("a rose", "is a", "rose is", "a rose") map( _.split( " " ).toSeq ) flatten
    
  • 仅当键(k)相同时,mapMergeMonoid函数才会出现将k和v的值相加。意味着未排序的列表将导致大量Map(string -> 1)

    您可以通过对Vector进行以下更改来对单词Vector进行排序:

    (Vector("a rose", "is a", "rose is", "a rose") map( _.split( " " ).toSeq ) flatten) sortWith(_.compareTo(_) < 0) 
    
  • 虽然foldMapV确实在Map[String, Int]中拆分了Vector中的所有短语或单词,但它似乎只返回已排序的IndexedSeq最左边的所需合并。根据我已经指出的排序的单词向量,我从Map(a -> 3)的实现中得到了结果frequencyMap。我倾向于使用foldMapV之外的其他内容,或者修改它以使frequencyMap起作用。对我来说似乎缺少的一点是它应用于一个大Map的函数结果的累积。我还会尝试比splitAt()调用更“标准”的头/尾递归(因为我不相信foldMapV处理isrose非常好。)< / p>