Scala入门:函数式编程实例WordCount

大数据学习路线图


点击这里观看厦门大学林子雨老师主讲《大数据技术原理与应用》授课视频
【版权声明】博客内容由厦门大学数据库实验室拥有版权,未经允许,请勿转载!
[返回Spark教程首页]

通过前面的函数式编程的基础知识的学习,现在让我们通过一个单词统计的实例来加深对前面知识的消化理解。

任务:按照函数式编程的风格,编写一个程序,对某个目录下所有文件中的单词进行词频统计
做法:请进入Linux系统,打开“终端”,进入Shell命令提示符状态,然后,在“/usr/local/scala/mycode”目录下,新建一个wordcount子目录,并在“/usr/local/scala/mycode/wordcount”目录下新建两个包含了一些语句的文本文件word1.txt和word2.txt(你可以在文本文件中随意输入一些单词,用空格隔开),我们会编写Scala程序对该目录下的这两个文件进行单词词频统计。
请在“/usr/local/scala/mycode”目录下新建一个test.scala文件,下面是test.scala中包含的进行词频统计的Scala程序代码:

import java.io.File
import scala.io.Source
object WordCount {
  def main(args: Array[String]): Unit = {
    val dirfile=new File("/usr/local/scala/mycode/wordcount")
    val files=dirfile.listFiles
    for(file <- files) println(file)
    val listFiles=files.toList
    val wordsMap=scala.collection.mutable.Map[String,Int]()
    listFiles.foreach( file =>Source.fromFile(file).getLines().foreach(line=>line.split(" ").
                  foreach(
                      word=>{
                        if (wordsMap.contains(word)) {
                          wordsMap(word)+=1
                        }else {
                          wordsMap+=(word->1)
                        }
                      }
                  )
            )

    )
    println(wordsMap)
    for((key,value)<-wordsMap) println(key+": "+value)
  }
}

然后,在Shell命令提示符状态下,运行下面命令执行:

cd /usr/local/scala/mycode  //要确保当前工作目录是mycode目录
scala test.scala

然后你就可以看到屏幕给出的词频统计结果。

下面我们在Scala解释器中运行前面几行代码,我们就可以看到每条语句运行产生的结果:

scala> import java.io.File
import java.io.File

scala> import scala.io.Source
import scala.io.Source

scala> val dirfile=new File("/usr/local/scala/mycode/wordcount")
dirfile: java.io.File = /usr/local/scala/mycode/wordcount

scala> val files=dirfile.listFiles
files: Array[java.io.File] = Array(/usr/local/scala/mycode/wordcount/word2.txt, /usr/local/scala/mycode/wordcount/word1.txt)

scala> for(file <- files) println(file)
/usr/local/scala/mycode/wordcount/word2.txt
/usr/local/scala/mycode/wordcount/word1.txt

scala> val listFiles=files.toList
listFiles: List[java.io.File] = List(/usr/local/scala/mycode/wordcount/word2.txt, /usr/local/scala/mycode/wordcount/word1.txt)

Array类的toList方法将连续存放的数组转换为递归存放的列表,上面的val listFiles=files.toList中,files是数组,files.toList得到一个列表。

listFiles.foreach(...)

上面语句会对列表listFiles中的每个元素进行遍历,取出每个元素(就是文件名/usr/local/scala/mycode/wordcount/word2.txt和 /usr/local/scala/mycode/wordcount/word1.txt),对每个元素按照括号中定义的逻辑进行处理。

file =>Source.fromFile(file).getLines().foreach(...)

上面语句是一个Lamda表达式,也就是一个匿名函数,这个匿名函数的功能是,把file变量和列表listFiles的元素进行绑定,每次遍历获取到列表listFiles中一个元素的值时,就把该元素的值赋值给file,比如第一次遍历获取到的值是“/usr/local/scala/mycode/wordcount/word1.txt”,就把这个值赋值给file变量。然后,把file作为输入参数,传递给Lamda表达式=>右边的函数体去处理,也就是,把file变量的值传递给Source.fromFile(file).getLines().foreach(...)去执行。Source.fromFile(file).getLines().foreach(...)会读取file文件(比如/usr/local/scala/mycode/wordcount/word1.txt),并调用getLines()方法获取该文件的所有行,并针对每行执行foreach()方法进行遍历,然后针对遍历到的当前一行,执行下面的处理逻辑:

line=>line.split(" ").foreach(...)

这也是个Lamda表达式,是个匿名函数,这个匿名函数的功能是,把line变量和Source.fromFile(file).getLines()得到的所有行的集合进行绑定,每次遍历获取到该集合中一个元素的值(也就是一行的内容)时,就把该元素的值(也就是一行的内容)赋值给line,然后,把line作为输入参数,传递给Lamda表达式=>右边的函数体去处理,也就是提交给line.split(" ").foreach(...)处理。line.split(" ").foreach(...)会对得到的一行内容进行单词切分,也就使用使用split()方法把一行语句切分成一个个单词,这些单词又构成一个集合,然后对这个集合中的每个元素(也就是每个单词),又通过foreach()方法进行遍历,对于遍历到的每个单词,都调用foreach()方法的圆括号中定义好的处理逻辑,该处理逻辑如下:

word=>{
       if (wordsMap.contains(word)) {
          wordsMap(word)+=1
       }else {
          wordsMap+=(word->1)
       }

上面的wordsMap是一个映射(Map)数据结构,每个映射中的条目都是一个<key,value>键值对,key是单词,value是单词出现的次数。上面这个处理逻辑的功能是,对于当前遍历到的某个单词,如果这个单词以前已经统计过,就把映射中以该单词为key的映射条目的value增加1。如果以前没有被统计过,则为这个单词新创建一个映射条目。

WordCount统计程序的最后两行代码,会把词频统计结果打印出来:

println(wordsMap)
for((key,value)<-wordsMap) println(key+": "+value)

子雨大数据之Spark入门
扫一扫访问本博客