2

我将一组行作为输入并跟踪字长的分布。输入中包含换行符的额外空格无关紧要。输入结束后,输出是基于文本的字长分布直方图:例如:“嘿,你好吗,嘿,我很好”

输出:1 - 0, 2 - 1, 3 - 5, 4 - 1, 5 - 0

其中(第一个字符是单词的长度,第二个是没有该长度的单词)。我已经写了

val lines = scala.io.Source.stdin.getLines
val words = lines.flatMap(_.split("\\W+"))

我想将相同长度的单词分组,然后将它们存储在迭代器或映射中

val list2 = words.groupby(e.length => e.length).mapValues(_.length) 

没有给我想要的结果。有什么建议么?

4

2 回答 2

6

你已经掌握了它,但你需要groupBy(e => e.length). e匿名函数 ( )的左侧 ( e => e.length) 应该是一个变量名,将用于集合中的每个项目(即每个单词)。单词也是如此e,我们根据单词的长度进行分组。

(另外,groupBy有一个大写的“B”)。

val list2 = words.groupBy(e => e.length).mapValues(_.length)

如果你想要你描述的输出,你可以跟进:

val vectorOfLengths = (1 to list2.keys.max).map(length => list2.getOrElse(length, 0))
// Vector(0, 1, 5, 1)
println(vectorOfLengths.zipWithIndex.map{case (count, length) => f"${length+1} - $count" }.mkString(", "))
// 1 - 0, 2 - 1, 3 - 5, 4 - 1

或者,嘿,视觉效果如何?

for ((count, length) <- vectorOfLengths.zipWithIndex)
  println(f"${length+1}: ${"#" * count}")
//   1:
//   2: #
//   3: #####
//   4: #

只是为了好玩,爱丽丝梦游仙境的视觉直方图怎么样?

val aliceLines = io.Source.fromFile("/Users/dhg/texts/alice.txt").getLines.toVector
val aliceWords = aliceLines.flatMap(_.split("\\W+"))
val aliceHist = aliceWords.groupBy(_.length).mapValues(_.length)
val aliceLengths = (1 to aliceHist.keys.max).map(aliceHist.getOrElse(_, 0))
for ((count, length) <- aliceLengths.zipWithIndex)
  println(f"${length+1}%2s: ${"#" * (count/100)}")

//     1: ###################
//     2: ##################################################
//     3: ############################################################################
//     4: #############################################################
//     5: ###################################
//     6: ######################
//     7: ##################
//     8: ########
//     9: ######
//    10: ###
//    11: #
//    12:
//    13:
//    14:
//    15:
//    16:
于 2015-01-26T17:01:19.293 回答
1

好吧,Scala 集合提供了 groupBy 方法,在Seq的情况下,它看起来像这样:

def groupBy[K](f: (A) ⇒ K): immutable.Map[K, Seq[A]]

这意味着它将一个函数应用于列表中的每个元素,并根据结果对它们进行分组。要按单词的长度对单词进行分组,该函数应该接受一个字符串并返回它的长度:

//words: Seq[String] = Seq(a, b, c, dd, eee, fff)
val byLength = words.groupBy{(w:String) => w.length}//Map(2 -> Seq(dd), 
                                                    //    1 -> Seq(a, b, c), 
                                                    //    3 -> Seq(eee, fff))

或者你可以把它更短一点,省略参数类型声明,编译器会理解你:

val byLength = words.groupBy(w => w.length)

甚至使用下划线占位符定义匿名函数:

val byLength = words.groupBy(_.length) //same thing

现在你可以得到指定长度的单词:

val singleCharacterWords = byLength(1) //Seq(a, b, c)

或检查地图是否包含一些长度

byLength.contains(1) //true
byLength.contains(5) //false

或遍历所有键:

byLength.foreach{
  case (length:Int, wordsGroup:Seq[String]) => 
    println(s"Words with length $length : ${wordsGroup.mkString(" ")}")
}
//Words with length 2 : dd
//Words with length 1 : b c
//Words with length 3 : eee fff

请参阅地图

于 2015-01-26T16:22:20.733 回答