第2章
Scala编程
Scala是当前热门的现代编程语言之一。它是编程语言界的凯迪拉克。它是一门强大且优美的语言。学会了它,对你的职业生涯大有裨益。
用不同的编程语言都可以编写大数据应用程序,比如Java、Python、C++、Scala等。Hadoop本身就是用Java编写的。尽管大多数的Hadoop应用程序都是用Java编写的,但它也支持用其他语言来编写。类似地,Spark是由Scala编写的,但是它也支持其他的语言,包括Scala、Java、Python和R。
对于开发大数据应用程序而言,Scala是一门很棒的语言。使用它有诸多好处。首先,开发者使用Scala能显著提高生产力。其次,它能帮助开发者减少bug,写出健壮的代码。最后,Spark就是用Scala编写的,因而对于开发Spark应用而言使用Scala是最自然的。
本章把Scala当作一门通用语言来进行介绍。本章的目的并不是让你成为Scala专家,而是让你有足够多的Scala知识从而能理解并使用Scala来编写Spark应用。本书的示例代码全部都由Scala编写,所以掌握Scala,将更容易理解本书的内容。如果你已经学会了Scala,那么你可以跳过这一章。
基于上述目的,本章只介绍Scala编程的基础知识。为了能更高效地使用Scala,了解函数式编程是很重要的,所以本章将首先介绍函数式编程。最后,将介绍如何编写一个单独的Scala应用程序。
2.1 函数式编程
函数式编程是一种编程范式,它把函数作为代码的基本构成元素,避免使用命令式编程中的可变变量以及循环等控制结构。它把计算当作对数学函数的求值,函数的输出完全依赖于传递给函数的参数值。程序就是由这些函数构成的。总之,在函数式编程语言中函数是一等公民。
在这几年,函数式编程引起了大量的关注。甚至一些主流语言(比如C++、Java和Python)都添加了对函数式编程的支持。它的流行主要有如下几个原因。
首先,使用函数式编程能极大地提升生产效率。相比于命令式编程,在解决同样的问题上,函数式编程只需要更少的代码。举例来说,在Java中一个用一百行代码实现的功能,用Scala来实现只需要10行或20行即可。函数式编程能带来5~10倍的生产效率提升。
其次,函数式编程更容易编写多并发或多线程的应用。随着多核、多CPU计算机的来临,编写多进程应用显得愈发重要。在当前提升单CPU晶体管数量已经越来越难的情况下,硬件产商开始采用增加CPU数量、增加内核数的方式来维持摩尔定律的有效性。现在多核计算机已经很普遍了。应用程序也需要利用多核带来的优势。相对于命令式编程语言,使用函数式编程语言更易于编写利用多核的应用程序。
再者,函数式编程能帮助你写出健壮的代码。它可以帮你避免一些常见的编程错误。而且一般来说,应用中bug的数量与代码和行数成正比。由于函数式编程相对于命令式编程通常代码行数更少,因此使用它将带来更少的bug。
最后,使用函数式编程语言更容易写出便于阅读、理解的优雅代码。如果运用得当,用函数式编程语言写出来的代码看上去是很漂亮的,既不复杂也不混乱。你将从你的代码中得到巨大的快乐和满足。
本节将会介绍函数式编程中的几个重要概念。
2.1.1 函数
函数是一段可以执行的代码。通过函数,程序员可以将一个大型的程序分割成一些方便管理的代码片段。在函数式编程中,应用程序就是构建在函数之上的。
尽管很多编程语言都支持函数的感念,但是函数式编程语言把函数当成一等公民。而且,函数是可以随意组合的,并没有其他的副作用。
一等公民
函数式编程把函数当成一等公民。函数拥有和变量、值一样的地位。函数可以像变量一样使用。如果你把函数式编程中的函数和命令式编程语言(比如C)中的函数对比,更容易理解这个概念。
命令式编程语言中变量和函数是区别对待的。举例来说,C语言中是不允许在函数内部定义函数的,它也不允许把一个函数当成参数传给另外一个函数。
函数式编程允许把函数当成参数传递给另一个函数。函数也可以当成返回值从另一个函数中返回。函数可以在任何地方定义,包括在其他函数内部。它可以写成匿名函数字面量,然后像字符串字面量一样作为参数传递给另一个函数。
可组合
函数式编程中,函数是可以随意组合的。函数组合指的是可以把一些简单的函数组合在一起从而创建一个复杂的函数,它是一个数学概念也是一个计算机科学概念。比如,两个可组合的函数可以组合成一个新函数。考虑下面两个数学函数。
f(x)=x*2
g(x)=x+2
函数f以一个数字作为输入,将它的两倍作为输出。函数g以一个数字作为输入,将它的值加2作为输出。
如下所示,可以将f和g组合为一个新的函数。
h(x)=f(g(x))=f(x+2)=(x+2)*2
对于同样的输入,函数h的效果和先调用函数g处理输入,然后将函数g的输出作为参数调用函数f是一样的。
为了解决复杂的问题,可以将它分解成几个小问题,函数组合在这种方式下是很有用的。对于每个小问题可以写若干个函数,最后再把它们组合起来,从而解决这个复杂的问题。
无副作用
在函数式编程中函数并没有什么副作用。函数的返回值完全依赖于传递给它的参数。函数的行为并不会随着时间的改变而改变。对于给定的参数值,无论调用这个函数多少次,始终返回同样的结果。换句话说,函数是无状态的,它既不依赖也不会改变任何全局变量的值。
函数的无副作用性有如下几个好处。首先,它们可以按任意顺序组合在一起。其次,便于理解代码。最后,使用这样的函数便于编写多线程的应用程序。
简单
函数式编程中的函数是很简单的。一个函数由几行代码构成,并且只做一件事。一个简单的函数是便于理解的。函数也保证了代码的健壮性以及高质量。
可组合的简单函数很适合用来实现复杂的算法,并且不容易出错。函数式编程鼓励人们不断把问题分解成若干个子问题,直到一个子问题能够用一个简单函数去解决为止。然后,把这些简单函数组合起来就能得到解决这个复杂问题的函数了。
2.1.2 不可变数据结构
函数式编程强调使用不可变数据结构。纯函数方式程序并不会使用任何可变数据结构或变量。换句话说,数据从不会在原地修改,这与命令式编程语言(比如C/C++、Java和Python)相比是不同的。没有函数式编程背景的人很难想象没有可变变量的程序是什么样子的。实际上,使用不可变数据结构写代码并非难事。
使用不可变数据结构具有如下好处。首先,它能减少bug。使用不可变数据结构编写的代码便于理解。另外,函数式编程语言的编译器会强制使用不可变的数据结构。因而,很多bug在编译期间就能捕获。
其次,使用不可变数据结构便于编写多线程应用程序。编写一个能充分利用多核的应用程序并不容易。竞争条件和数据损坏在多线程应用程序中是常见的问题。使用不可变数据结构有助于避免这些问题。
2.1.3 一切皆表达式
在函数式编程中,每一个语句都是一个表达式,都会有返回值。比如Scala中的if-else控制结构就是一个有返回值的表达式。这与可以在if-else中写多个语句的命令式编程语言显著不同。
这一特性有助于不用可变变量编写应用程序。