它写出的程序像动态语言一样简洁,但事实上它确是严格意义上的静态语言。">Scala 就像一位武林中的集大成者,将过去几十年计算机语言发展历史中的精萃集于一身,化繁为简,为程序员们提供了一种新的选择。作者希望通过这个系列,可以为大家介绍 Scala 语言的特性,和 Scala 语言给我们带来的关于编程思想的新的思考。
在本系列的第一篇文章 《使用递归的方式去思考》中,作者并没有首先介绍 Scala 的语法,这样做有两个原因:一是因为过多的陷入语法的细节当中,会分散读者的注意力,反而忽略了对于基本概念,基本思想的理解;二是因为 Scala 语法非常简洁,拥有其他语言编程经验的程序员很容易读懂 Scala 代码。现在我们将回过头来,从基本的语法开始学习 Scala 语言。大家会发现 Scala 语言异常精炼,实现同样功能的程序,在代码量上,使用 Scala 实现通常比 Java 实现少一半或者更多。短小精悍的代码常常意味着更易懂,更易维护。本文将为大家介绍 Scala 语言的基本语法,帮助大家写出自己的第一个 Scala 程序。
开发环境
学习 Scala,最方便的方式是安装一个 Scala 的 IDE(集成开发环境),Typesafe 公司开发了一款基于 Eclipse 的 IDE。该款 IDE 为 Scala 初学者提供了一个方便的功能:Worksheet。像 Python 或者 Ruby 提供的 RELP(Read-Eval-Print Loop)一样,Worksheet 允许用户输入 Scala 表达式,保存后立即得到程序运行结果,非常方便用户体验 Scala 语言的各种特性。
Hello World
让我们以经典的 Hello World 程序开始,只需在 Worksheet 里输入 println("Hello World!")保存即可,在该语句的右边就可以立刻看到程序执行结果。
Worksheet 也可以被当作一个简单的计算器,试着输入一些算式,保存。
图 1. Worksheet
变量和函数
当然,我们不能仅仅满足使用 Scala 来进行一些算术运算。写稍微复杂一点的程序,我们就需要定义变量和函数。Scala 为定义变量提供了两种语法。使用 val定义常量,一经定义后,该变量名不能被重新赋值。使用 var定义变量,可被重新赋值。在 Scala 中,鼓励使用 val,除非你有明确的需求使用 var。对于 Java 程序员来说,刚开始可能会觉得有违直觉,但习惯后你会发现,大多数场合下我们都不需要 var,一个可变的变量。
清单 1. 定义变量
val x = 0 var y = 1 y = 2 // 给常量赋值会出现编译错误 // x = 3 // 显式指定变量类型 val x1: Int = 0 var y1: Int = 0
仔细观察上述代码,我们会有两个发现:
定义变量时没有指定变量类型。这是否意味着 Scala 是和 Python 或者 Ruby 一样的动态类型语言呢?恰恰相反,Scala 是严格意义上的静态类型语言,由于其采用了先进的类型推断(Type Inference)技术,程序员不需要在写程序时显式指定类型,编译器会根据上下文推断出类型信息。比如变量 x被赋值为 0,0 是一个整型,所以 x的类型被推断出为整型。当然,Scala 语言也允许显示指定类型,如变量 x1,y1的定义。一般情况下,我们应尽量使用 Scala 提供的类型推断系统使代码看上去更加简洁。
另一个发现是程序语句结尾没有分号,这也是 Scala 中约定俗成的编程习惯。大多数情况下分号都是可省的,如果你需要将两条语句写在同一行,则需要用分号分开它们。
函数的定义也非常简单,使用关键字 def,后跟函数名和参数列表,如果不是递归函数可以选择省略函数返回类型。Scala 还支持定义匿名函数,匿名函数由参数列表,箭头连接符和函数体组成。函数在 Scala 中属于一级对象,它可以作为参数传递给其他函数,可以作为另一个函数的返回值,或者赋给一个变量。在下面的示例代码中,定义的匿名函数被赋给变量 cube。匿名函数使用起来非常方便,比如 List对象中的一些方法需要传入一个简单的函数作为参数,我们当然可以定义一个函数,然后再传给 List对象中的方法,但使用匿名函数,程序看上去更加简洁。
清单 2. 定义函数
// 定义函数 def square(x: Int): Int = x * x // 如果不是递归函数,函数返回类型可省略 def sum_of_square(x: Int, y: Int) = square(x) + square(y) sum_of_square(2, 3) // 定义匿名函数 val cube = (x: Int) => x * x *x cube(3) // 使用匿名函数,返回列表中的正数 List(-2, -1, 0, 1, 2, 3).filter(x => x > 0)
让我们再来和 Java 中对应的函数定义语法比较一下。首先,函数体没有像 Java 那样放在 {}里。Scala 中的一条语句其实是一个表达式,函数的执行过程就是对函数体内的表达式的求值过程,最后一条表达式的值就是函数的返回值。如果函数体只包含一条表达式,则可以省略 {}。其次,没有显示的 return语句,最后一条表达式的值会自动返回给函数的调用者。
和 Java 不同,在 Scala 中,函数内部还可以定义其他函数。比如上面的程序中,如果用户只对 sum_of_square 函数感兴趣,则我们可以将 square 函数定义为内部函数,实现细节的隐藏。
清单 3. 定义内部函数
def sum_of_square(x: Int, y: Int): Int = { def square(x: Int) = x * x square(x) + square(y) }