Scala 指南

开始精彩的Scala旅程

 下一页

Hello World

Scala 是一门 函数式面向对象语言。他运行在Java虚拟机上.

本指南是用来介绍Scala强大的功能。同时你可以亲身实践他们。

点击左边的Run按钮试试。左边的运行框可以在远程编译和运行任何Scala代码。运行的结果将展示在下面对文本框中。你也可以试着编辑这些代码,重新运行。(初次运行可能会有些慢,不过第二次就很快了)

使用方向键或者空格来翻页

 下一页

基础开始

目录(基础)

  • 表达式和值
  • 函数是一等公民
  • 借贷模式
  • 按名称传递参数
  • 定义类
  • 鸭子类型
  • 柯里化
  • 范型
  • Traits
 概览  下一页

表达式和值

在Scala中,几乎所有的语言元素都是表达式。

println("hello wolrd")

是一个表达式,

"hello"+" world"

也是一个表达式。

可以通过val定义一个常量,亦可以通过var定义一个变量。推荐多使用常量。

 目录  下一页

函数是一等公民

可以使用def来定义一个函数。函数体是一个表达式。

使用Block表达式的时候,默认最后一行的返回是返回值,无需显式指定。

函数还可以像值一样,赋值给var或val。因此函数也可以作为参数传给另一个函数。

 上一页  目录  下一页

借贷模式

由于函数可以像值一样作为参数传递,所以可以方便的实现借贷模式。

这个例子是从/proc/self/stat文件中读取当前进程的pid。

withScanner封装了try-finally块,所以调用者不用再close

注:当表达式没有返回值时,默认返回Unit。

 上一页  目录  下一页

按名称传递参数

这个例子演示了按名称传递参数,由于有除以0,所以运行该程序会产生异常。

试着将

def log(msg: String)

修改为

def log(msg: => String)

由按值传递修改为按名称传递后将不会产生异常。

因为log函数的参数是按名称传递,参数会等到实际使用的时候才会计算,所以被跳过。

按名称传递参数可以减少不必要的计算和异常。

 上一页  目录  下一页

定义类

可以用class关键字来定义类。并通过new来创建类。

在定义类时可以定义字段,如firstName,lastName。这样做还可以自动生成构造函数。

可以在类中通过def定义函数var和val定义字段

函数名是任何字符如+,-,*,/。

试着将

obama.age_=(51)

简化为

obama.age = 51

这样的简化更像调用一个变量。

 上一页  目录  下一页

鸭子类型

走起来像鸭子,叫起来像鸭子,就是鸭子。

这个例子中使用

{ def close(): Unit }

作为参数类型。因此任何含有close()的函数的类都可以作为参数。

不必使用继承这种不够灵活的特性。

 上一页  目录  下一页

柯里化

这个例子和上面的功能相同。不同的是使用了柯里化(Currying)技术。

def add(x:Int, y:Int) = x + y

是普通的函数

def add(x:Int) = (y:Int) => x + y

是柯里化后的函数,相当于返回一个匿名函数表达式。

 def add(x:Int)(y:Int) = x + y

是简化写法

柯里化可以让我们构造出更像原生语言提供的功能的代码

试着将例子中的withclose(...)(...)换成withclose(...){...}

 上一页  目录  下一页

范型

之前的例子可以使用泛型变得更简洁更灵活。

试着将

"123456"

修改为

123456

虽然msg由String类型变为Int类型,但是由于使用了泛型,代码依旧可以正常运行。

 上一页  目录  下一页

Traits

Traits就像是有函数体的Interface。使用with关键字来混入。

这个例子是给java.util.ArrayList添加了foreach的功能。

试着再在with ForEachAble[Int]后面加上

with JsonAble

给list添加toJson的能力

 上一页  目录  下一页

函数式开始

目录(函数式)

  • 模式匹配
  • Case Class
  • 函数式的威力
  • 函数式真正的威力
  • Word Count
  • 尾递归
  • 更强大的For循环
  • Option
  • Lazy初始化
 概览  下一页

模式匹配

模式匹配是类似switch-case特性,但更加灵活;也类似if-else,但更加简约。

这个例子展示的使用用模式匹配实现斐波那契。 使用case来匹配参数,如果case _,则可以匹配任何参数。

试着将

case n: Int

修改为

case n: Int if (n > 1)

case后添加if语句判断,这样修改当输入负数时,就不会无限循环。

模式匹配也可以匹配类型,在case _ 前加上

case n: String => fibonacci(n.toInt)

这样就可以匹配String类型

 目录  下一页

Case Class

case class 顾名思义就是为case语句专门设计的类, 在普通类的基础上添加了和类名一直的工厂方法, 还添加了hashcode,equals和toString等方法。

由于使用了require(n >= 0)来检验参数,如果使用负数计算,将会抛出异常。

 上一页  目录  下一页

函数式的威力

这个例子是用指令式编程判断一个List中是否含有奇数。

试着将

containsOdd(list)

替换为

list.exists((x: Int) => x % 2 ==1)

通过将函数作为参数,可以使程序更简洁。还可以再简化为

list.exists(_ % 2 == 1)

可以用_替代参数

相比于Ruby等动态语言,这威力来自于科学而不是魔法

 上一页  目录  下一页

函数式真正的威力

函数式除了能简化代码外,更重要的是他关注的是InputOutput,函数本身没有副作用。

就是Unix pipeline一样,简单的命令组合在一起威力无穷。

如果你喜欢Unix pipeline的方式,你一定也会喜欢函数式编程。

这个例子是用函数式的代码模拟

cat file | grep 'warn' | grep '2013' | wc

List的filter方法接受一个过滤函数,返回一个新的List,作为下一个方法的输入。

 上一页  目录  下一页

Word Count

Word Count是一个MapReduce的一个经典示例。在函数式编程中,Word Count最直观的实现方法也是MapReduce。

这个例子介绍了List的两个重要的高阶方法mapreduceLeft

map接受一个转换函数,返回转换结果。

reduceLeft接受一个合并函数,依次遍历合并。

使用高阶方法可以代替大部分需要循环的操作,使代码更清晰

 上一页  目录  下一页

尾递归

尾递归是递归的一种,特点在于会在函数的最末调用自身。尾递归是函数式编程的常见写法。

这个例子是foldLeft的尾递归实现。foldLeft和reduceLeft相比更常用,多一个初始参数。

当用List做match case时。可以使用 :: 来解构。返回第一个元素head和剩余元素tail。

注:尾递归会在编译期优化,因此不用担心递归造成的栈溢出问题。

 上一页  目录  下一页

更强大的For循环

循环语句是指令式编程的常见语句,Scala对其加以改进,成为适应函数式风格的利器。

For循环也是有返回值的,返回的是一个List。在每一轮迭代中加入yield,yield后的值可以加入到List中。

这个例子是使用for循环代替map函数。

 上一页  目录  下一页

Option

Scala提供了Option机制来解决,代码中不断检查null的问题。

这个例子包装了getProperty方法,使其返回一个Option。 这样就可以不再漫无目的地null检查。只要Option类型的值即可。

使用pattern match来检查是常见做法。也可以使用getOrElse来提供当为None时的默认值。

给力的是Option还可以看作是最大长度为1的List,List的强大功能都可以使用。

再见 NullException

 上一页  目录  下一页

Lazy初始化

Lazy可以延迟初始化字段。加上lazy的字段会在第一次访问的时候初始化,而不是类初始化的时候初始化。

这个例子是从github获得Scala的版本号,由于访问网络需要较多时间。可以使用lazy来延迟获取。 防止可能的浪费。

Lazy非常适合于初始化非常耗时的场景

 上一页  目录  下一页

并发开始

目录(并发)

  • 使用Actor
  • Actor更简化的用法
  • Actor原理
  • 同步返回
  • 异步返回
  • 并行集合
  • 并行wordcount
  • 远程Actor
 概览  下一页

使用Actor

Actor是Scala的并发模型。在2.10之后的版本中,使用Akka作为其推荐Actor实现。

Actor是类似线程的实体,有一个邮箱。 Actor可以通过system.actorOf来创建,receive获取邮箱消息,向邮箱发送消息。

这个例子是一个EchoServer,接受信息并打印。

 目录  下一页

Actor更简化的用法

可以通过更简化的办法声明Actor。

通过

akka.actor.ActorDSL

中的actor函数。这个函数可以接受一个Actor的构造器Act,启动并返回Actor。

 上一页  目录  下一页

Actor原理

Actor比线程轻量。在Scala中可以创建数以百万级的Actor。奥秘在于Actor直接可以复用线程

Actor和线程是不同的抽象,他们的对应关系是由Dispatcher决定的。

这个例子创建4个Actor,每次调用的时候打印自身线程名称。

可以发现Actor和线程之间没有一对一的对应关系。一个Actor可以使用多个线程,一个线程也会被多个Actor复用。

 上一页  目录  下一页

同步返回

Actor非常适合于较耗时的操作。比如获取网络资源。

这个例子通过调用ask函数来获取一个Future。

在Actor内部通过 sender ! 传递结果。

Future像Option一样有很多高阶方法,可以使用foreach查看结果。

 上一页  目录  下一页

异步返回

异步操作可以最大发挥效能。Scala的Futrue很强大,可以异步返回。

可以实现Futrue的onComplete方法。当Futrue结束的时候就会回调。

在调用ask的时候,可以设定超时。

 上一页  目录  下一页

并行集合

这个例子是访问若干URL,并记录时间。如果能并行访问,就可以大幅提高性能。

尝试将

urls.map

修改为

urls.par.map

这样每个map中的函数都可以并发执行。

当函数式和并发结合,就会这样让人兴奋。

 上一页  目录  下一页

并行wordcount

这个例子是访问若干URL,并记录时间。

并行集合支持大部分集合的功能。

在前面有一个wordcount例子,也可以用并行集合加以实现。

不增加程序复杂性,却能大幅提高利用多核的能力。

 上一页  目录  下一页

远程Actor

Actor是并发模型,也使用于分布式。

这个例子创建一个Echo服务器,通过actorOf来注册自己。

然后再创建一个client,通过akka url来寻址。

除了是通过url创建的,其他使用的方法和普通Actor一样。

 上一页  目录  下一页

实践开始

目录(实践)

  • 使用Java
  • 相等性
  • 抽取器
  • 记忆模式
  • 隐式转换
  • DSL
  • 测试
  • Simple Build Tool
 概览  下一页

使用Java

Scala和Java可以非常方便的互操作,前面已经有大量Scala直接使用Java的例子。

同样Java也可以使用Scala。这个例子演示使用@BeanProperty注解来生成Java Style的Bean。

尝试将

var name: String

修改为

@BeanProperty var name: String

这样就给bean添加了getter/setter。 Apache BeanUtils就可以正常工作。

 目录  下一页

相等性

在Scala中==等效于equals,这一点和Java不同。更自然一些。

写一个完全正确的equal函数并不容易,这个例子也有子类会不对称的Bug。

尝试将class修改为case class,并删除equals函数。

case类会自动生成正确的equals函数。

 上一页  目录  下一页

抽取器

抽取器可以帮助模式匹配进行解构。

这个例子是构建一个Email抽取器,只要实现unapply函数就可以了。

Scala的正则表达式会自带抽取器,可以抽取出一个List。List里的元素是匹配()里的表达式。

抽取器很有用,短短的例子里就有两处使用抽取器:

case user :: domain :: Nil

解构List

case Email(user, domain)

解构Email。

 上一页  目录  下一页

记忆模式

记忆模式可以解决手动编写存取cache代码的麻烦。

这个例子中,memo可以将一个不含cache函数,包装成一个含有cache功能的。

还是斐波那契的例子,通过cache可以使性能提高。

尝试将

fibonacci_(n - 1) + fibonacci_(n - 2)

修改为

memo(fibonacci_)(n - 1) + memo(fibonacci_)(n - 2)

可以提高更多性能。

 上一页  目录  下一页

隐式转换

implicit可以定义一个转换函数,可以在使用相应类型的时候自动转换。

这个例子可以将String自动转换为Date类型。隐式转换时实现DSL的重要工具。

 上一页  目录  下一页

DSL

DSL是Scala最强大武器,可以使一些描述性代码变得极为简单。

这个例子是使用DSL生成JSON。Scala很多看似是语言级的特性也是用DSL做到的。

自己编写DSL有点复杂,但使用起来非常方便。

 上一页  目录  下一页

测试

Scala可以使用Spec2,ScalaTest来测试, DSL可以使测试更方便。

这个例子是测试一个阶乘函数。使用should/in来建立测试用例。

测试是默认并发执行的。

 上一页  目录  下一页

Simple Build Tool

SBT是Scala的最佳编译工具,在他的帮助下,

你甚至不需要安装除JRE外的任何东西,来开发Scala。

例如你想在自己的机器上执行这个Scala-Tour,可以执行左边的命令

 上一页  目录  下一页

关于

这个指南源自于作者对Scala的热爱和对传播Scala的愿望

其他资料:

  • Scala School!: Twitter的Scala教学
  • A Tour of Scala:官方Scala Tour
  • Scala By Example:更多的Scala例程
  • Scala Cheatsheet:一眼望穿Scala基本语法
  • Functional Programming Principles in Scala:Coursera 的Scala课程

from: http://zh.scala-tour/#/basics-contents

更多推荐

Scala 指南