文章目录

  • 1. Scala有多cool
    • 1.1. 速度!
    • 1.2. 易用的数据结构
    • 1.3. OOP+FP
    • 1.4. 动态+静态
    • 1.5. DSL
    • 1.6. 够复杂
    • 1.7. 够有趣
    • 1.8. 开发社区
  • 2. lang
    • 2.1. 和Java的异同
      • 2.1.1. 语法
      • 2.1.2. 库
    • 2.2. 变量
      • 2.2.1. 保留字
      • 2.2.2. 变量标识
      • 2.2.3. 变量定义
        • 2.2.3.1 val, var
        • 2.2.3.2 花样定义
        • 2.2.3.3 lazy, val, def的区别
    • 2.3. 基本类型
      • 2.3.1. Int
      • 2.3.2. Char
    • 2.4. BigInt
    • 2.5. 字符串
      • 2.5.1. 类型转换
      • 2.5.2. StringBuilder
      • 2.5.3. 文本格式化
    • 2.6. Null, None, Nil, Nothing
    • 2.7. ==和eq
    • 2.8. Option[T]
      • 2.8.1. 概念
      • 2.8.2. 使用
      • 2.8.3. 例子
    • 2.9. 区分<-,=>,->
    • 2.10. match..case(switch)
      • 2.10.1. 和switch..case的区别
      • 2.10.2. 匹配数据类型
      • 2.10.3. 命令行参数解析例子
      • 2.10.4. 使用case的递归函数
      • 2.10.5. 变量匹配
      • 2.10.6. case..if条件匹配
    • 2.11. try..catch..finally
    • 2.12. require
    • 2.13. main方法
      • 2.13.1. Application
    • 2.14. package, import
      • 2.14.1. import
      • 2.14.2. package
      • 2.14.3. 包对象
    • 2.15. if..else
    • 2.16. 循环操作
      • 2.16.1. for
      • 2.16.1. for .. yield
      • 2.16.2. foreach
      • 2.16.3. forall
      • 2.16.4. reduceLeft
      • 2.16.5. foldLeft scanLeft
      • 2.16.6. scanLeft
      • 2.16.7. take drop splitAt
      • 2.16.8. takeWhile, dropWhile, span
      • 2.16.9. break、continue
    • 2.17. 操作符重载
    • 2.18. 系统定义scala._
    • 2.19. implicit隐式转换
      • 2.19.1. 类型转换
      • 2.19.2. 例子:阶乘n!
      • 2.19.3. 例子:cout
      • 2.19.4. 例子:定义?:
      • 2.19.5. 已有Object加新方法
    • 2.20. type做alias
    • 2.21. 泛型
      • 2.21.1. 函数中的泛型:
      • 2.21.2. 类中的泛型:
      • 2.21.3. 泛型qsort例子
      • 2.21.4. 泛型add例子
      • 2.21.5. 泛型定义type
      • 2.21.6. @specialized
    • 2.22. 枚举Enum
  • 3. FP
    • 3.1. 函数
      • 3.1.1. 函数定义
      • 3.1.2. 映射式定义
      • 3.1.3. 特殊函数名 + - * /
      • 3.1.4. 缺省参数、命名参数
      • 3.1.5. 函数调用
      • 3.1.6. 匿名函数
      • 3.1.7. 偏函数
      • 3.1.8. “_”匿名参数
      • 3.1.9. 变长参数 *
      • 3.1.10. lazy参数
    • 3.2. 函数式编程
      • 3.2.1. 风格
      • 3.2.2. 函数作为参数
      • 3.2.3. 函数作为返回值
      • 3.2.4. Curry化
      • 3.2.5. 递归
      • 3.2.6. 尾-递归
  • 4. OOP
    • 4.1. 类class
      • 4.1.1. 定义
      • 4.1.2. 构造方法
      • 4.1.3. override
      • 4.1.4. object单例对象
      • 4.1.5. 静态方法
      • 4.1.6. case class(条件类)
      • 4.1.7. case object(条件单例对象)
      • 4.1.8. 枚举
      • 4.1.9. 属性和Bean
      • 4.1.10. 反射
    • 4.2. trait超级接口
      • 4.2.1. trait使用
      • 4.2.2. mixin
    • 4.3. 协变和逆变(co-|contra-)variance
      • 4.3.1. 概念
      • 4.3.2. 类型上下界
      • 4.3.3. 协变、逆变结合上下界
  • 5. util包
    • 5.1. 架构
    • 5.2. 集合Array,List,Tuple
      • 5.2.1. 定义和初始化
        • 5.2.1.1 Array
        • 5.2.1.2 List
        • 5.2.1.3 Vector
        • 5.2.1.4 Tuple
        • 5.2.1.5 Range
        • 5.2.1.6 Stream
        • 5.2.1.7 Stack Queue
      • 5.2.2. 使用(map, flatMap, filter, exists等)
        • 5.2.2.1 map
        • 5.2.2.2 filter filterNot
        • 5.2.2.3 partition span splitAt groupBy
        • 5.2.2.4 foreach
        • 5.2.2.5 exists
        • 5.2.2.6 find
        • 5.2.2.7 sorted sortWith sortBy
        • 5.2.2.8 distinct
        • 5.2.2.9 flatMap
        • 5.2.2.10 indices,zipWithIndex
        • 5.2.2.11 take drop splitAt
        • 5.2.2.12 count
        • 5.2.2.13 updated patch
        • 5.2.2.14 reverse reverseMap
        • 5.2.2.15 contains startsWith endWith
        • 5.2.2.16 集合运算
        • 5.2.2.17 殊途同归
        • 5.2.2.18 其他
      • 5.2.3. 数组元素定位
      • 5.2.4. view
      • 5.2.5. 和Java集合间的转换(scalaj)
    • 5.3. Map
      • 5.3.1. 定义Map
      • 5.3.2. 不可变Map(缺省)
      • 5.3.3. 可变Map
      • 5.3.4. Java的HashMap
      • 5.3.5. 读取所有元素
      • 5.3.6. 多值Map
    • 5.4. Set
      • 5.4.1. 定义
      • 5.4.2. 逻辑运算
      • 5.4.3. 可变BitSet
    • 5.5. Iterator
    • 5.6. Paralllel collection
  • 6. io
    • 6.1. 文件I/O
      • 6.1.1. 读文件
      • 6.1.2. 写文件
      • 6.1.3. 复制文件
      • 6.1.4. 全目录扫描
    • 6.2. 网络I/O
  • 7. actor
    • 7.1. actor模型
    • 7.2. 多核计算
    • 7.3. Actor用法
    • 7.4. 方式1:接受receive
    • 7.5. 方式2:接受react, loop
    • 7.6. REPL接受消息
    • 7.7. actor最佳实践
      • 7.7.1. 不阻塞actor
      • 7.7.2. actor之间用且仅用消息来通讯
      • 7.7.3. 采用不可变消息
      • 7.7.4. 让消息自说明
    • 7.8. 不同jvm间的消息访问
    • 7.9. STM
  • 8. misc
    • 8.1. xml
      • 8.1.1. 生成
      • 8.1.2. xml文件
      • 8.1.3. 读取:
      • 8.1.4. 访问属性
      • 8.1.5. 格式化输出
    • 8.2. json
    • 8.3. Configgy
    • 8.4. 正则表达式regex
    • 8.5. GUI
      • 8.5.1. java方式
      • 8.5.2. scala方式
  • 9. 附录
    • 9.1. stackoverflow的scala教程
    • 9.2. ProjectEuler
    • 9.3. Scala问答
    • 9.4. rosettacode
    • 9.5. 编译、mvn、SBT
    • 9.6. Scala水平划分
    • 9.7. Scala适合的领域
    • 9.8. Twitter的Scala School

引用:http://qiujj.com/static/Scala-Handbook.htm

Scala的向后兼容性没有Java那么谨慎,2.8+相对于2.7有很大变化,本文档针对2.8+

“和别人分享你的知识,那才是永恒之道”——somebody

前言:

这只是份简单的笔记,本不应敝履自珍,但手痒难耐还是写点废话在前面。

书籍浩如烟海,技术方面的书也汗牛充栋,可惜我们的阅读速度和理解力、记忆力太有限,往往费力学懂的知识转眼就变得非常陌生,“博闻强志、过目不忘”者毕竟罕见。对于大部分人来说,即便昔日信手拈来的东西,时间久了也会毫无头绪。所以知识不在于曾经学过多少,而在于你记住并还能运用多少。

“好记性不如烂笔头”——近年来我慢慢习惯把费力学习的东西都做一个笔记,一是在学习的过程中加深印象,毕竟技术学习不同于欣赏娱乐大片和浏览娱乐新闻看个过眼烟云;二是便于学而“时习之”,书上的东西一般是针对不同技术背景的读者,有很多作者费力用墨之处对你来说纯属废话,而他一笔带过的地方恰恰让你困惑不已。一本读书笔记相当于你对书的“注解”。

Scala很好玩很有趣,但绝对不是一门简单易懂的编程语言(Java从1.0到7.0,一直做++;而Scala不光做++,也做–)。对于从Java或者其他FP走过来的人,Scala有很多“别扭”的用法,很难记清楚用正确。学Scala,最佳的做法是把它用到日常的应用开发中,不断加深记忆。但即便你准备这么做了,手头没有一份方便的备查材料,刚开始也会步履艰难。我在使用的过程中也有这个体会,所以才不怨其烦地把一些学来并尝试过的东西记在本文档中备查,以便之后能行云流水地“玩转”它。整个写笔记的过程,没有孤寂,而是沉浸在学习新知的兴奋和快意中,希望这种快乐也能放大传递给更多的人。

个人认为,对于一门编程语言使用中的查阅,大致有几个阶段:查教程(tutorial)——》查手册(handbook)——》查自己写的库。这个材料,不是严格的教程,或手册,而是介于这两者间。Scala目前已经出版了几本书,这些书从各自的角度解读Scala,比如Scala作者的“Programming Scala”,应该是最权威的Scala书。但好书不一定好读,一是太厚,二是无趣,不如自制的来得贴心。正如数学问题要用公式表达最清楚,编程问题得用图表和代码表示才最清楚,这二者也是本文中使用最多的表达方式,我尽量采用简短的代码来说明问题(简短代码也能说明很多事情,广受赞誉的Effective Java基本没有超过一页的程序代码)。能够熟练使用Java的程序员,参考本笔记,应该可以自如地开始着手写Scala程序。

若能给同样对Scala感兴趣的IT人一些帮助,本人必感欣慰。

——JamesQiu

1. Scala有多cool

“Put productivity & creativity back in the hands of developers” Scala is a tour de force of language design.

1.1. 速度!

Java的运行速度

——基于JVM,和Java运行速度相当。看看Ruby、Perl、Python对大项目运行效率的无奈,就知道有个好的编译器(Scalac)和运行时(JVM)是多么美好。

Python/Ruby的编程速度

——有更多的内建库和数据结构,编程就更快,Scala在完全继承Java和.NET的标准库的基础上,还扩展了更丰富有用的函数库。看看C++、D、Go等语言库的发展情况(不是匮乏就是混乱),就知道从头创建如Java、.NET这般庞大全面的类库并非易事;

类库和运行速度有关系吗?——很大程度上有,众多专家已经在类库中准备了充分优化的稳定算法,Scala对Java Collection算法进行直接包装或者直接调用,如果没有丰富的类库,你在项目周期内免不了摘抄一些不一定靠谱的算法和功能代码,这些代码极有可能在运行时给你带来麻烦。使用类库算法,不用担忧自造轮子的运行效率。

Scala是静态语言,Scalac和Javac是同一作者,编译成.class后运行于JVM平台,近20年那么多大公司投入进行的优化也不是白搭。对于大部分的应用来说,使用Scala不用再顾虑运行速度,它可能不是最快,但至少逼近Java,而不像Groovy、JRuby、Jython那般与Java有高达数十倍的效率差距。

1.2. 易用的数据结构

List-Map-Tuple及其丰富特性支持让你解决数据结构问题时游刃有余。

List(1,31,4,3,53,4,234) filter (10<) filter (100>)  // List(31, 53)
  • 1
val (a,b) = List(1, 31,4,3,53,4,234) partition (10>) // a=List(1,4,3,4), b=List(31,53,234)
  • 1
def info(p:Person) = (name, age, email) // info._1, info._2, info._3
  • 1

1.3. OOP+FP

  • 适当地选用OOP或者FP,能够使表达相对另一种更加清晰准确。
  • 实际可见的生产力在于:一个应用中的部分代码尤其是高知识凝聚的代码如数学函数和设计模式,一般来说不会自己编写,而是会来自于现成的Java库,或者其他语言,或者伪代码。我们可以很容易地把过程语言、面向对象语言、函数式语言中的代码“翻译”成Scala代码。试想如果我们要把Haskell或者Lisp的某个尾递归算法翻译成Java代码,还得多花点时间;而要把C++的代码翻译成Hashkell,同样也不简单。Scala的混血性给我们的实际使用提供了便利。
  • 语言特色能够塑造编程者的思维: C++也能使用抽象基类设计多重继承,但Java的接口引导你走得更远;Java也能设计类型安全的静态方法(final static),但Scala鼓励你这样做并逐步从OOP到达FP的彼岸,而且来去自如。

1.4. 动态+静态

Scala虽然是一门彻头彻底的静态语言,但又具备了现代动态语言的很多方便和灵活:

  • 不需要冗余的类型声明
  • 可以在已有类上增加新方法(implicit转换和Dynamic trait)
  • 可以把不继承共同父类的不同类型对象传到方法中
  • 可以做静态语言的Refactoring
  • 不用象动态语言那样测试代码比业务代码还多
  • 代码自动完成(REPL和IDE)
  • 编译静态语言的性能
  • Read-Eval-Print Loop交互解释器(注:Linux下的用户体验远好于Windows下)

1.5. DSL

Scala可以把xml/html处理、数学公式表达、SQL查询等包装的更优雅、更合理,为使用者提供更好的API。这也使Scala的程序也更可读,从而更易于维护。

1.6. 够复杂

不同的思考模式:Java是先写后想,Scala是先想后写(其实FP大都如此)。

Scala相比于Java,可能达不到10倍的代码精简;但读Scala代码的效率一般只有Java的1/10——可见Java是一门没有多少特例的简单语言,而Scala则不然。

你不要指望把Scala作为初学者的第一门编程语言,这门语言甚至不是初级程序员能够掌控的——换句话说,能够读懂和写Scala代码,说明你是一个不折不扣的资深程序员,或者更准确一点,是资深Java程序员。

ð
简单的Java(Morse码,定型的玩具车) 复杂的Scala(智能机,乐高积木)

1.7. 够有趣

还看这句话:“Put productivity & creativity back in the hands of developers”。其实不仅限于Scala,对于所有的编程语言来说,一门语言是否“好玩”有趣,能否激起创作欲,才是最关键的,这比语言风格、运行速度、工具支持、社区文化都来得重要。

回想我使用过的语言,C、C++,只有在学习“图形学”课程做作业的时候给我“好玩”和编程过瘾的感觉;VB、Delphi、Lotus Script/Formular、JavaScript,重来没有给过我“好玩”的感觉,而Java是在之前很长一段时间内让我觉得最“好玩”的语言,用它编游戏、做模式识别的作业、做产品……,乐在其中。但是Java也许久没有再给我这种编程过瘾的感觉了。之前发现Groovy的时候,我以为又找到一门好玩的语言了,但我一段时间使用之后,发现不是我的菜(Perl、Python、Ruby也如此);我不是说那些我不觉得好玩的语言不好,有其他很多人觉得他们非常“好玩”,并且用它们创建了无数杀手级的、伟大的、有用的程序。

有些人对一门语言会玩一辈子,就像Lisp、Haskell和Smalltalk的拥趸;而有些人会不断寻找下一个玩意儿,就像原来玩Java的一些人发现更好玩的Ruby和Python之后,倒戈狂喷Java,力挺后者;Groovy/Grails的玩家在很短的时间里面,写了无数的扩展和Plugin应用;学习Scala,能很多好玩的地方,能用它有激情地去写一些振奋人心的应用出来!

1.8. 开发社区

Scala开发/用户社区气氛良好,基本都是资深开发者以及有一定经验的用户,不会碰到太弱智的事(提问、争吵),除了语言和工具开源免费,最权威和最好的书也都是免费的(包括Lift社区)

2. lang

2.1. 和Java的异同

2.1.1. 语法

Java++:增加的语法 Java–:删减的语法
纯OO 静态成员
操作符重载 原生数据类型
closure break、continue
使用trait进行mixin组合 接口
existential type(_) 通配符List<?>, import pkg.*;
抽象类型 (type T) 原始类型 class C1 {…}
模式匹配 enum枚举

注:

existential type——和Java互操作时进行对应

Iterator<? extends Component> --> Iterator[T] { type T <: Component }或者Iterator[_]

2.1.2. 库

以下功能通过库的形式提供:

  • assert
  • enum
  • property
  • event
  • actor
  • resource control(自动释放)
def using[T <: { def close() }] (res:T)(block:T=>Unit) = {

try { block(res) } finally { if(res!=null) res.close }}

using (new BufferedReader(new FileReader(path))) { f=> println(f.readLine) }

不用每次使用Java中的finally

val f = new BufferedReader(new FileReader(path)

try { println(f.readLine) } finally { if (f!=null) f.close }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • query

2.2. 变量

2.2.1. 保留字

abstract case catch class def

do else extends false final

finally for if implicit import

match new null object override

package private protected requires return

sealed super this throw trait

try true type val var

while with yield

_ : = => <- <: <% >: # @

Scala调用Java的方法时,会碰到有Scala的保留字,如Thread.yield()

这在Scala中是非法的,专门有个解决办法,写成: Thread.yield()

注意:没有break和continue

2.2.2. 变量标识

这些标识在Java中是非法的,在Scala中是合法的,可以当作函数名使用,使接口更加DSL:

val empty_? = true

val + = “hello”

val yield = 10

val ** = “power”

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.2.3. 变量定义

2.2.3.1 val, var

var 可变,可重新赋值,赋值为"_"表示缺省值(0, false, null),例如:

var d:Double = _      // d = 0.0

var i:Int = _ // i = 0

var s:String = _ // s = null

var t:T = _ // 泛型T对应的默认值

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

val 不可变,相当于const/final,但如果val为数组或者List,val的元素可以赋值;

val pi = 3.            // 相当于3.0d

val pi = 3.f // 相当于3.0f

  • 1
  • 2
  • 3

提示:向函数式风格推进的一个方式,就是尝试不用任何var来定义变量。

2.2.3.2 花样定义

和Python一样方便的赋值方式:

val x,y = 0                // 赋同一初始值

val (x,y) = (10, “hello”) // 同时定义多个变量,注意:val x,y=10,“hello” 是错误的

  • 1
  • 2
  • 3

更花:

val x::y = List(1,2,3,4)             // x = 1, y = List(2,3,4)

val List(a,b,c) = List(1,2,3) // a = 1, b = 2, c = 3

  • 1
  • 2
  • 3

进一步花样:

val Array(a, b, _, _, c @ _*) = Array(1, 2, 3, 4, 5, 6, 7)  // 也可以用List,Seq

a // 1

b // 2

c // Array(5, 6, 7), _*匹配0个到多个

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用正则表达式定义:

val regex = "(\d+)/(\d+)/(\d+)".r

val regex(year, month, day) = “2010/1/13”

// year: String = 2010

// month: String = 1

// day: String = 13

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.2.3.3 lazy, val, def的区别

val 定义时就一次求值完成,保持不变 val f = 10+20 // 30
lazy 定义时不求值,第一次使用时完成求值,保持不变 lazy f = 10+20 // f // 30
def 定义时不求值,每次使用时都重新求值 def f = 10+20 // 30def t = System. currentTimeMillis // 每次不一样
scala> val f1 = System.currentTimeMillis

f1: Long = 1279682740376 // 马上求值

scala> f1

res94: Long = 1279682740376 // 之后保持不变

scala> lazy val f2 = System.currentTimeMillis

f2: Long = <lazy> // 定义时不求值

scala> System.currentTimeMillis

res95: Long = 1279682764297

scala> f2

res96: Long = 1279682766545 // 第一次使用时求值,注意:6545 > 4297

scala> f2

res97: Long = 1279682766545 // 之后保持不变

scala> def f3 = System.currentTimeMillis

f3: Long

scala> f3

res98: Long = 1279682784478 // 每次求值

scala> f3

res99: Long = 1279682785352 // 每次求值

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

2.3. 基本类型

尽量使用大写形式: Int, Long, Double, Byte, Short, Char, Float, Double, Boolean

编译时Scala自动对应到Java原始类型,提高运行效率。Unit对应java的void

用 asInstanseOf[T]方法来强制转换类型:

def i = 10.asInstanceOf[Double]                                  // i: Double = 10.0

List(‘A’,‘B’,‘C’).map(c=>(c+32).asInstanceOf[Char]) // List(‘a’,‘b’,‘c’)

  • 1
  • 2
  • 3

用isInstanceOf[T]方法来判断类型:

val b = 10.isInstanceOf[Int]                             // true
  • 1

而在match … case 中可以直接判断而不用此方法。

用Any统一了原生类型和引用类型。

2.3.1. Int

-3 abs                                    // 3

-3 max -2 // -2

-3 min -2 // -3

1.4 round // 1 四舍五入

1.6 round // 2 四舍五入

1.1 ceil // 2.0 天花板

1.1 floor // 1.0 地板

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

无++,–操作,但可以+=, -=, 如下:

var i = 0

i++ // 报错,无此操作

i+=1 // 1

i-- // 报错,无此操作

i-=1 // 0

def even(n:Int) = 0==(n & 1)

def odd(n:Int) = !even(n)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.3.2. Char

String可以转化为List[Char]

在String上做循环,其实就是对String中的每一个Char做操作,如:

"12345" map (toInt)                    // (49,50,51,52,53)

“jamesqiu” max // ‘u’

“jamesqiu” min // ‘a’

(‘a’ to ‘f’) map (_.toString*3) // (aaa, bbb, ccc, ddd, eee, fff)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.4. BigInt

可以表示很大的整数:

BigInt(10000000000000000000000000)          // 报错

BigInt(“10000000000000000000000000”) // scala.math.BigInt = 10000000000000000000000000

  • 1
  • 2
  • 3

例如:

def  fac(n:Int):BigInt = if (n==0) 1 else fac(n-1)*n

fac(1000)

  • 1
  • 2
  • 3

或者写成:

def fac2(n:Int) = ((1:BigInt) to n).product
// res1: BigInt = 9332621544394415268169923885626670049071596826438......000000000000000000
  • 1
  • 2

2.5. 字符串

“…” 或者 “”"…""""

println("""|Welcome to Ultamix 3000.
       |Type "HELP" for help.""".stripMargin)

  • 1
  • 2
  • 3

输出:

Welcome to Ultamix 3000.

Type “HELP” for help.

scala中,字符串除了可以+,也可以*

"abc" * 3                                                    // "abcabcabc"

“abc” * 0 // “”

  • 1
  • 2
  • 3

例子:

"google".reverse                                       // "elgoog"

“abc”.reverse.reverse==“abc” // true

  • 1
  • 2
  • 3

例子:

"Hello" map (_.toUpper)                          // 相当于 "Hello".toUpperCase
  • 1

2.5.1. 类型转换

"101".toInt                                                 // 101,无需 Integer.parseInt("101");

“3.14”.toFloat // 3.14f

101.toString

3.14.toString

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

转换整个列表:

List("1","2","3") map (_.toInt)                    // List(1,2,3)
  • 1

或者

List("1","2","3") map Integer.parseInt            // List(1,2,3)
  • 1

2.5.2. StringBuilder

val sb = new StringBuilder

sb += ‘H’

sb ++= “ello”

sb.toString // “Hello”

sb clear // StringBuilder()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.5.3. 文本格式化

使用java.text.MessageFormat.format:

val msg = java.text.MessageFormat.format("At {1,time} on {1,date}, there was {2} on planet {0}.","Hoth", new java.util.Date(), "a disturbance in the Force")
  • 1
  • 2
  • 3

输出

At 17:50:34 on 2010-7-20, there was a disturbance in the Force on planet Hoth.

方法2:

"my name is %s, age is %d." format ("james", 30)          // my name is james, age is 30.
  • 1

注意:format还可以这么用

"%s-%d:%1s is %2d." format ("james", 30)                 // james-30:james is 30.

“%2d age’s man %1s: %2$d” format (“james”, 30) // 30 age’s man james: 30

  • 1
  • 2
  • 3
  • 4

2.6. Null, None, Nil, Nothing

Null Trait,其唯一实例为null,是AnyRef的子类,不是 AnyVal的子类
Nothing Trait,所有类型(包括AnyRef和AnyVal)的子类,没有实例
None Option的两个子类之一,另一个是Some,用于安全的函数返回值
Unit 无返回值的函数的类型,和java的void对应
Nil 长度为0的List

2.7. ==和eq

Scala的很智能,他知道对于数值类型要调用Java中的,ref类型要调用Java的equals()

"hello"=="Hello".toLowerCase()
  • 1

在java中为false,在scala中为true

Scala的==总是内容对比

基本类型Int,Double, 比值
其他类型 相当于A.equals(B)

eq才是引用对比

例如:

val s1,s2 = "hello"

val s3 = new String(“hello”)

s1==s2 // true

s1 eq s2 // true

s1==s3 // true 值相同

s1 eq s3 // false 不是同一个引用

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.8. Option[T]

2.8.1. 概念

  • Option[T]可以是任意类型或者空,但一旦声明类型就不能改变;
  • Option[T]可完美替代Java中的null,可以是Some[T]或者None;
  • Option实现了map, flatMap, and filter 接口,允许在 'for’循环里使用它;

函数返回值能被统一处理了:

没有Option的日子 现在
def find(id:Long):Person = … def find(id:Long):Option[Person] = …
返回Person或者null 返回Some[Person]或者None
返回null不特殊处理会抛:NullPointerExceptions 返回值直接getOrElse或者列表操作
类比:Java的Stringx.split返回null 类比:Java的Stringx.split返回new String[0]
结论:函数永远不要返回null值,如果输入有问题或者抛异常,返回Option[T]

参数有效性检查没有那么烦人了:

没有Option的日子 现在
def blank(s:String) = if (s==null) false else { s.toList.forall(_.isWhitespace) } def blank(s:String) = Option(s).toList.forall(.forall(.isWhitespace))
结论:尽可能地不要浪费代码去检测输入,包装成Option[T]来统一处理

2.8.2. 使用

Some(3).getOrElse(4)                               // 3

None.getOrElse4) // 4

  • 1
  • 2
  • 3
  • 4

例如打印key=3的value:

写法1:

def p(map:Map[Int,Int]) = println(map(3))

p(Map(1->100,2->200)) // 抛异常

  • 1
  • 2
  • 3

写法2:

def p(map:Map[Int,Int]) = println(map get 3 getOrElse "...")

p(Map(1->100,2->200)) // …

p(Map(1->100,3->300)) // 300

  • 1
  • 2
  • 3
  • 4
  • 5

2.8.3. 例子

例子1:

  def m(k:Int) = {
Map((1,100),(2,200),(3,300)) get(k) match {case Some(v) =&gt;k + ": " + vcase None =&gt;"not found"}

}

def main(args : Array[String]) : Unit = {

println(m(1))     // 100println(m(2))     // 200println(m(3))     // 300println(m(4))    // "not found"println(m(-1))    // "not found"

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

例子2:

val l = List(Some(100), None, Some(200), Some(120), None)

for (Some(s) <- l) yield s // List(100, 200, 120)

  • 1
  • 2
  • 3

l.flatMap (x=>x) // List(100, 200, 120)
  • 1

例子3: Option结合flatMap

def toint(s:String) =

try { Some(Integer.parseInt(s)) } catch { case e:Exception => None }

List(“123”, “12a”, “45”) flatMap toint // List(123, 45)

List(“123”, “12a”, “45”) map toint // List(Some(123), None, Some(45))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.9. 区分<-,=>,->

<- for (i <- 0 until 100) 用于for循环, 符号∈的象形
=> List(1,2,3).map(x=> x*x)((i:Int)=>i*i)(5) // 25 用于匿名函数,相当于Ruby的 |x|,Groovy的{x-> x*x}也可用在import中定义别名:import javax.swing.{JFrame=>jf}
-> Map(1->“a”,2->“b”) 用于Map初始化, 也可以不用->而写成 Map((1,“a”),(2,“b”))

2.10. match…case(switch)

2.10.1. 和switch…case的区别

Java里面的写法:

switch(n) {
<span class="token keyword">case</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span><span class="token keyword">case</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span><span class="token keyword">default</span><span class="token operator">:</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

变成Scala写法:

def m(n:String) =

n match {

case "a" | "b" =&gt; ... // 这个比较好case "c" =&gt; ...case _ =&gt; ...

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

每个case…=>结束不用写break了,_相当于default

2.10.2. 匹配数据类型

match 可以很简单地匹配数据类型(不需要isInstanceOf[T]):

def f(v:Any) = v match {
case null =&gt; "null"case i:Int =&gt; i*100case s:String =&gt; scase _ =&gt; "others"

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注意:上面case中的i、s都叫模式变量

f(null)          // "null"

f(5) // 500

f(“hello”) // “hello”

f(3.14) // “others”

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

注意:自定义类型如果也要匹配,需要用case class

2.10.3. 命令行参数解析例子

/** Basic command line parsing. */
object Main {var verbose = false  // 记录标识,以便能同时对-h和-v做出响应

def main(args: Array[String]) {
for (a <- args) a match {
case “-h” | “-help” =>
println(“Usage: scala Main [-help|-verbose]”)
case “-v” | “-verbose” =>
verbose = true
case x => // 这里x是临时变量
println(“Unknown option: '” + x + “’”)
}
if (verbose) println(“How are you today?”)
}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.10.4. 使用case的递归函数

写法1:

def fac(n:Int):Int = n match {

case 0=>1

case _=>n*fac(n-1)

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

写法2(使用映射式函数):

def fac: Int=>Int = {
case 0=&gt; 1case n=&gt; n*fac(n-1)

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

写法3(使用尾递归):

 def fac: (Int,Int)=>Int = {case (0,y) => ycase (x,y) => fac(x-1, x*y)}

fac(5,1) // 120

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

写法4(reduceLeft+!):

def fac(n:Int) = 1 to n reduceLeft(_*_)
implicit def foo(n:Int) = new { def ! = fac(n) }
5!      // 120
  • 1
  • 2
  • 3

写法5:(最简洁高效)

def fac(n:Int) = (1:BigInt) to n product
fac(5) // 120
  • 1
  • 2

2.10.5. 变量匹配

常量匹配很简单,即case后跟的都是常量;变量匹配需要注意,case后跟的是match里面的临时变量,而不是其他变量名:

3 match {
case i => println("i=" + i) // 这里i是模式变量(临时变量),就是3
}
val a = 10
20 match { case a => 1 } // 1, a是模式变量,不是10
  • 1
  • 2
  • 3
  • 4
  • 5

为了使用变量a,必须用`a`:

20 match { case `a` => 1; case b => -1 } // -1,`a`是变量10
  • 1

或者用大写的变量:

val A = 10
20 match { case A => 1; case b => -1 } // -1,大写A是变量10
  • 1
  • 2

2.10.6. case…if条件匹配

写法1:

(1 to 20) foreach {
case x if (x % 15 == 0) =&gt; printf("%2d:15n\n",x)case x if (x % 3 == 0)  =&gt; printf("%2d:3n\n",x)case x if (x % 5 == 0)  =&gt; printf("%2d:5n\n",x)case x =&gt; printf("%2d\n",x)

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

写法2:

(1 to 20) map (x=> (x%3,x%5) match {case (0,0) => printf("%2d:15n\n",x)case (0,_) => printf("%2d:3n\n",x)case (_,0) => printf("%2d:5n\n",x)case (_,_) => printf("%2d\n",x)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.11. try…catch…finally

var f = openFile()

try {

f = new FileReader(“input.txt”)

} catch {

case ex: FileNotFoundException => // Handle missing file

case ex: IOException => // Handle other I/O error

} finally {

f.close()

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.12. require

def f(n:Int) = { require(n!=0); 1.0/n }

def f(n:Int) = { require(n!=0, “n can’t be zero”); 1.0/n }

f(0)

// java.lang.IllegalArgumentException: requirement failed: n can’t be zero

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.13. main方法

Scala的main方法(包括所有类似java的static方法)必须定义在一个object内:

object Test1 {
def main(args: Array[String]) {println("hello world")}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

编译:

fsc Test1.scala // 常驻内存编译服务器,第一次转载之后比scalac快

运行:

scala Test1.scala // 方式1,没有输出

scala -cp e:\scala\lib\scala-library.jar Test1 // 方式2,慢

java -cp e:\scala\lib\scala-library.jar Test1 // 方式3,快

  • 1
  • 2
  • 3
  • 4
  • 5

如果文件不是utf8编码,执行需要使用.scala文件的编码,如:

scala -encoding gbk test.scala
  • 1

2.13.1. Application

不带命令行参数的简化main方法:

object app1 extends Application {
println("hello world")

}

  • 1
  • 2
  • 3
  • 4
  • 5

2.14. package, import

2.14.1. import

Scala的import可以只在局部作用域内生效;

可以格式 “import javax.swing.{JFrame=>jf}”来声明类型的别名。

jf.show()

  • import javax.swing._
  • import java.util.{List, Map}
  • import java.util., java.io.

Scala 缺省导入如下包:

  • java.lang.*
  • scala.*
  • scala.Predef

由于Scala的package可以是相对路径下定义,有可能命名冲突,可以用:

import root.java.lang.Long

2.14.2. package

package com.wr3 { // C# 和Ruby的方式,也可以改用Java的方式
// import java.nio._ // "*" 是scala的正常函数名,所以用_class c1 {def m1() { println("c1.m1()") }}object o1 {def main(args: Array[String]) {println("o1.main()")new c1().m1()}}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

编译:

fsc package.scala
  • 1

运行:

java com.wr3.o1 // 方式1

scala com.wr3.o1 // 方式2

  • 1
  • 2
  • 3

2.14.3. 包对象

Scala2.8+支持包对象(package object),除了和2.8之前一样可以有下级的object和class,还可以直接有下级变量和函数,例如:

-------------------------------------------------------------- foo.scala--------------------------------

package p0

package object p1 {

val a = 10def b = "hello " + adef main(args:Array[String]):Unit = printf("%s", p0.p1.b)

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

p1就是一个包对象,a和b就是包p1直属的常量和函数,

$fsc foo.scala 命令产生如下class:

./p0/p1/package.class

调用:

scala p0.p1.package
  • 1

2.15. if…else

没有java的:

b = (x>y) ? 100 : -1
  • 1

就用:

if (x>y) 100 else -1
  • 1

2.16. 循环操作

map m->m
flatMap m->n
indices m->m
foreach m->Unit
for (… if …) yield m->n
collect { case … if … => … } m->n
filter, filterNot m->n
take m->n
takeWhile m->n
forall m->1 (true|false)
reduceLeft, foldLeft m->1
scanLeft m->m+1
exists m->1 (true|false)
find m->1 (或者None)
count m->1
span, partition m->2

2.16.1. for

循环中的变量不用定义,如:

for(i<-1 to 10; j=i*i) println(j)

for (s <- ss) foo(s)

for (i <- 0 to n) foo(i) // 包含n,即Range(0,1,2,…,n,n+1)

for (i <- 0 until n) foo(i) // 不包含n,即Range(0,1,2,3,…,n)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

例如:

for (n<-List(1,2,3,4) if n%2==1) yield n*n  // List(1, 9)

for (n<-Array(1,2,3,4) if n%2==1) yield n*n // Array(1, 9)

  • 1
  • 2
  • 3

注意:如果if后面不止一条语句,要用{…}包裹。

var s = 0; for (i <- 0 until 100) { s += i } // s = 4950
  • 1

等价于不用for的写法:

List(1,2,3,4).filter(_%2==1).map(n => n*n)
  • 1

如果for条件是多行,不能用(),要用{}

for(i<-0 to 5; j<-0 to 2) yield i+j

// Vector(0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5 , 6, 5, 6, 7)

for{i<-0 to 5

j<-0 to 2} yield i+j

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

例子1:

// 边长21以内所有符合勾股弦的三角形:

def triangle(n: Int) = for {

x <- 1 to 21

y <- x to 21

z <- y to 21

if x * x + y * y == z * z

} yield (x, y, z)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

结果:

// Vector((3,4,5), (5,12,13), (6,8,10), (8,15,17), (9,12,15), (12,16,20))

2.16.1. for … yield

把每次循环的结果“”进一个集合(类型和循环内的一致)

for {子句} yield {循环体}

正确:

for (e<-List(1,2,3)) yield (e*e) // List(1,4,9)

for {e<-List(1,2,3)} yield { e*e } // List(1,4,9)

for {e<-List(1,2,3)} yield e*e // List(1,4,9)

  • 1
  • 2
  • 3
  • 4
  • 5

错误:

for (e<-List(1,2,3)) { yield e*e } // 语法错误,yield不能在任何括号内
  • 1

2.16.2. foreach

List(1,2,3).foreach(println)
1
2
3
  • 1
  • 2
  • 3
  • 4

也可以写成:

(1 to 3).foreach(println)
  • 1

或者

(1 until 4) foreach println
  • 1

或者

Range(1,3) foreach println
  • 1

注意:

  • to包含,until不包含(最后的数)
  • 都可以写步长,如:
 1 to (11,2) // 1,3,5,7,9,11 步长为2
 1 to 11 by 21 until (11,2) // 1,3,5,7,91 until 11 by 2

val r = (1 to 10 by 4) // (1,5,9), r.start=r.first=1; r.end=10, r.last=9

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 也可以是BigInt
 (1:BigInt) to 3
  • 1

2.16.3. forall

“所有都符合”——相当于 A1 && A2 && A3 && … && Ai && … && An

(1 to 3) forall (0<) // true

(-1 to 3) forall (0<) // false

  • 1
  • 2
  • 3

又如:

def isPrime(n:Int) = 2 until n forall (n%_!=0)

for (i<-1 to 100 if isPrime(i)) println(i)

(2 to 20) partition (isPrime _) // (2,3,5,7,11,13,17,19), (4,6,8,9,10,12,14,15,16,18,20)

  • 1
  • 2
  • 3
  • 4
  • 5

也可直接调用BigInt的内部方法:

(2 to 20) partition (BigInt(_) isProbablePrime(10))
  • 1

// 注:isProbablePrime©中c越大,是质数的概率越高,10对应概率:1 - 1/(2**10) = 0.999

2.16.4. reduceLeft

reduceLeft 方法首先应用于前两个元素,然后再应用于第一次应用的结果和接下去的一个元素,等等,直至整个列表。例如

计算阶乘:

def fac(n: Int) = 1 to n reduceLeft(*)

fac(5) // 543*2 = 120

  • 1
  • 2
  • 3

相当于:

((((12)3)4)5)
  • 1

计算sum:

List(2,4,6).reduceLeft(+) // 12
  • 1

相当于:

​ ((2+4)+6)

取max:

List(1,4,9,6,7).reduceLeft( (x,y)=> if (x>y) x else y ) // 9
  • 1

或者简化为:

List(1,4,9,6,7).reduceLeft(_ max _) // 9
  • 1

相当于:

((((1 max 4) max 9) max 6) max 7)
  • 1

2.16.5. foldLeft scanLeft

累加或累乘

def sum(L: List[Int]): Int = {
    var result = 0for (item &lt;- L)result += itemresult

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

更scalable的写法:

def sum(L: Seq[Int]) = L.foldLeft(0)((a, b) => a + b)

def sum(L: Seq[Int]) = L.foldLeft(0)(_ + _)

def sum(L: List[Int]) = (0/:L){_ + _}

  • 1
  • 2
  • 3
  • 4
  • 5

调用:

sum(List(1,3,5,7)) // 16
  • 1

乘法:

def multiply(L: Seq[Int]) = L.foldLeft(1)(_ * _)

multiply(Seq(1,2,3,4,5)) // 120

multiply(1 until 5+1) // 120

  • 1
  • 2
  • 3
  • 4
  • 5

2.16.6. scanLeft

List(1,2,3,4,5).scanLeft(0)(+) // (0,1,3,6,10,15)
  • 1

相当于:

(0,(0+1),(0+1+2),(0+1+2+3),(0+1+2+3+4),(0+1+2+3+4+5))

List(1,2,3,4,5).scanLeft(1)(*) // (1,2,6,24,120)
  • 1

相当于

(1, 11, 112, 1123, 11234, 11234*5)

注:

  • (z /: List(a, b, c))(op) 相当于 op(op(op(z, a), b), c)

简单来说:加法用0,乘法用1

  • (List(a, b, c) :\ z) (op) equals op(a, op(b, op(c, z)))

2.16.7. take drop splitAt

1 to 10 by 2 take 3 // Range(1, 3, 5)

1 to 10 by 2 drop 3 // Range(7, 9)

1 to 10 by 2 splitAt 2 // (Range(1, 3),Range(5, 7, 9))

  • 1
  • 2
  • 3
  • 4
  • 5

例子:前10个质数

def prime(n:Int) = (! ((2 to math.sqrt(n).toInt) exists (i=> n%i==0)))

2 to 100 filter prime take 10

  • 1
  • 2
  • 3

2.16.8. takeWhile, dropWhile, span

while语句的缩写,

takeWhile (…) 等价于:while (…) { take }
dropWhile (…) 等价于:while (…) { drop }
span (…) 等价于:while (…) { take; drop }
1 to 10 takeWhile (_<5) // (1,2,3,4)

1 to 10 takeWhile (_>5) // ()

10 to (1,-1) takeWhile(_>6) // (10,9,8,7)

1 to 10 takeWhile (n=>n*n<25) // (1, 2, 3, 4)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果不想直接用集合元素做条件,可以定义var变量来判断:

例如,从1 to 10取前几个数字,要求累加不超过30:

var sum=0;

val rt = (1 to 10).takeWhile(e=> {sum=sum+e;sum<30}) // Range(1, 2, 3, 4, 5, 6, 7)

  • 1
  • 2
  • 3

注意:takeWhile中的函数要返回Boolean,sum<30要放在最后;

1 to 10 dropWhile (_<5) // (5,6,7,8,9,10)

1 to 10 dropWhile (n=>n*n<25) // (5,6,7,8,9,10)

1 to 10 span (_<5) // ((1,2,3,4),(5,6,7,8)

List(1,0,1,0) span (_>0) // ((1), (0,1,0))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注意,partition是和span完全不同的操作

List(1,0,1,0) partition (_>0) // ((1,1),(0,0))
  • 1

2.16.9. break、continue

Scala中没有break和continue语法!需要break得加辅助boolean变量,或者用库(continue没有).

例子1:打印’a’ to 'z’的前10个

var i=0; val rt = for(e<-('a' to 'z') if {i=i+1;i<=10}) printf("%d:%s\n",i,e)
  • 1

或者:

('a' to 'z').slice(0,10).foreach(println)
  • 1

例子2:1 to 100 和小于1000的数

var (n,sum)=(0,0); for(i<-0 to 100 if (sum+i<1000)) { n=i; sum+=i }
// n = 44, sum = 990
  • 1
  • 2

例子3:使用库来实现break

import scala.util.control.Breaks._

for(e<-1 to 10) { val e2 = e*e; if (e2>10) break; println(e) }

  • 1
  • 2
  • 3

2.17. 操作符重载

注意:其实Scala没有操作符,更谈不上操作符重载;±/*都是方法名,如1+2其实是(1).+(2)

object operator {
class complex(val i:Int, val j:Int) { // val 是必须的def + (c2: complex) = {new complex (i+c2.i, j+c2.j)}override def toString() = { "(" + i + "," + j + ")" }}def main(args:Array[String]) = {val c1 = new complex(3, 10)val c2 = new complex(5, 70)printf("%s + %s = %s", c1, c2, c1+c2)}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

编译:fsc operator.scala

运行:java operator // (3,10) + (5,70) = (8,80)

2.18. 系统定义scala._

scala._自动加载,只有发生类名冲突时才需要带上scala.包名。

scala.AnyValue 所有基本类型的根 Int,Char,Boolean,Double,Unit
scala.AnyRef 所有引用类型的根 相当于java的java.lang.Object
scala.Null 所有引用类型的子类
scala.Nothing 所有全部类型的子类
scala.List 不可变的List scala特色的不可变List
scala.Int scala中可以用int作为别名 Double,Float等类似

2.19. implicit隐式转换

用途:

  • 把一种object类型安全地自动转换成另一种object类型;
  • 不改动已有class设计即可添加新的方法;

2.19.1. 类型转换

implicit def foo(s:String):Int = Integer.parseInt(s) // 需要时把String->Int

def add(a:Int, b:Int) = a+b

add(“100”,8) // 108, 先把"100"隐式转换为100

  • 1
  • 2
  • 3
  • 4
  • 5

2.19.2. 例子:阶乘n!

第一步:写函数

def factorial(x: Int) = 1 to n reduceLeft(*)
  • 1

第二步:定义 “!” 函数

  class m1(n: Int) {
    def ! = factorial(n)}

implicit def m2(n:Int) = new m1(n) // 隐式转换,即在需要时把n转换为new m1(n)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意:上面可以用匿名类简化为:

implicit def m2(n:Int) = new { def ! = factorial(n) }
  • 1

第三步:使用

val n = 100

printf("%d! = %s\n", n, (n!)) // n! 相当于 new m1(n).!()

println(10!)

  • 1
  • 2
  • 3
  • 4
  • 5

2.19.3. 例子:cout

import java.io._

class C1(p:PrintStream) {

def << (a:Any) = {

p.print(a)

p.flush

p

}

}

implicit def foo(p:PrintStream) = new C1§

val endl = ‘\n’

System.out<<“hello”<<" world"<<endl

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

2.19.4. 例子:定义?:

implicit def elvisOperator[T](alt: T) = new {def ?:[A >: T](pred: A) = if (pred == null) alt else pred
}

null ?: “” // “”
“abc” ?: “” // “abc”
10 ?: 0 // 10
(null ?: 0).asInstanceOf[Int] // 0

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.19.5. 已有Object加新方法

object NewMethod {

// 定义新方法join()

implicit def foo1T = new {

def join(s:String) = list.mkString(s)

}

// 测试

def main(args : Array[String]) : Unit = {

Console println List(1,2,3,4,5).join(" - ") // " 1 - 2 - 3 - 4 – 5"

}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

解释:

编译器发现List没有join(String)方法,就发查找代码中有没有定义在implicit def xx(List)内的 join(String)方法,如果有就调用这个。

为Int增加乘方操作:

def pow(n:Int, m:Int):Int = if (m==0) 1 else n*pow(n,m-1)

implicit def foo(n:Int) = new {

def **(m:Int) = pow(n,m)

}

2**10 // 1024

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

例子2:定义如ruby的10.next

implicit def foo(n:Int) = new { def next = n+1 }

10.next // 11

  • 1
  • 2
  • 3

2.20. type做alias

相当于C语言的类型定义typedef,建立新的数据类型名(别名);在一个函数中用到同名类时可以起不同的别名

例如:

type JDate = java.util.Date

type SDate = java.sql.Date

val d1 = new JDate() // 相当于 val d = new java.util.Date()

val d2 = new SDate() // 相当于 val d = new java.sql.Date()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

注意:type也可以做泛型

2.21. 泛型

2.21.1. 函数中的泛型:

def foo[T](a:T) = println("value is " + a)

foo(10) // “value is 10”

foo(3.14) // “value is 3.14”

foo(“hello”) // “value is hello”

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.21.2. 类中的泛型:

class C1[T] {

private var v:T = _ // 初始值为T类型的缺省值

def set(v1:T) = { v = v1 }

def get = v

}

new C1[Int].set(10).get // 10

new C1[String].set(“hello”).get // “hello”

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.21.3. 泛型qsort例子

def qsortT <% Ordered[T]: List[T] = if (a.size<=1) a else {

val m = a(a.size/2)

qsort(a.filter(m>)) ++ a.filter(m==) ++ qsort(a.filter(m<))

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

调用:

val a = List(1, 3, 6, 2, 0, 9, 8, 7, 2)

qsort[Int] // 注意2 a

val b = List(1.0, 3.3, 6.2, 6.3, 0, 9.5, 8.7, 7.3, 2.2)

qsort[Double] // 注意2 b

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

或者使用自带库:

List(1, 3, 6, 2, 0, 9, 8, 7, 2) sortWith(<)

List(1.0, 3.3, 6.2, 6.3, 0, 9.5, 8.7, 7.3, 2.2).sortWith(<)

  • 1
  • 2
  • 3

2.21.4. 泛型add例子

// 采用implicit parameters

def addT(implicit n:Numeric[T]) = {

n.plus(x,y) // 或者用 import n._; x + y

}

add(1,2) // 3

add(1,2.14) // 3.14

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

再如,求max:

def max2[T](implicit n:Numeric[T]) = {

import n._

if (x > y) x else y

}

max2(2, 3.14) // 3.14

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.21.5. 泛型定义type

abstract class C1 {
type Tval e:T

}

abstract class C2 {

type Tval list:List[T]def len = list.length

}

def m1(e1:Int) = new C1 {

type T = Intval e = e1

}

def m2(e1:List[Int]) = new C2 {

type T = Intval list = e1

}

Console println m1(10) // 10

Console println m2(List(1,2,3,4,5)).len // 5

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

注意:type也可以做数据类型的alias,类似C语言中的typedef

2.21.6. @specialized

def test@specialized(Int,Double) T  = x match { case i:Int => "int"; case _ => "other" }
  • 1

没有@specialized之前,是编译成Object的代码;用了@specialized,变成IntTest(),DoubleTest(),…

编译后的文件尺寸扩充了,但性能也强了,不用box,unbox了

2.22. 枚举Enum

Scala没有在语言层面定义Enumeration,而是在库中实现:

例子1:

object Color extends Enumeration {

type Color = Value

val RED, GREEN, BLUE, WHITE, BLACK = Value

}

Color.RED // Color.Value = RED

import Color._

val colorful = Color.values filterNot (e=> eWHITE || eBLACK)

colorful foreach println // RED\nGREEN\nBLUE

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

例子2:

object Color extends Enumeration {

val RED = Value(“红色”)

val GREEN = Value(“绿色”)

val BLUE = Value(“蓝色”)

val WHITE = Value(“黑”)

val BLASK = Value(“白”)

}

Color.RED // Color.Value = 红色

import Color._

val colorful = Color.values filterNot (List(“黑”,“白”) contains _.toString)

colorful foreach println //红色\n绿色\n蓝色

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

3. FP

3.1. 函数

函数的地位和一般的变量是同等的,可以作为函数的参数,可以作为返回值。

传入函数的任何输入是只读的,比如一个字符串,不会被改变,只会返回一个新的字符串。

Java里面的一个问题就是很多只用到一次的private方法,没有和使用它的方法紧密结合;Scala可以在函数里面定义函数,很好地解决了这个问题。

3.1.1. 函数定义

函数和方法一般用def定义;也可以用val定义匿名函数,或者定义函数别名。

def m0(x:Int) = x*x

val m1 = (x:Int)=> x*x // ()是必须的

val m2 = {x:Int=> x*x} // 不用(), 用{}

m0(10) // 100

m1(10) // 100

m2(10) // 100

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

不需要返回值的函数,可以使用def f() {…},永远返回Unit(即使使用了return),即:

def f() {...}  //等价于 def f():Unit = {...}
  • 1

例如:

def f() { return "hello world" }

f() // Unit,而不是 “hello world”

  • 1
  • 2
  • 3
  • 4

需要返回值的函数,用 def f() = {…} 或者 def f = {…}

def f() = { "hello world" }

f() // “hello world”

def f = { “hello world” } // f是匿名函数 {“hello world”}的别名

f // “hello world”

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

三种定义方式的区别:

def f() { return … } 调用:f, f()皆可 始终返回:Unit
def f() = … 调用:f, f()皆可 返回Unit或者值
def f = … 调用:f 返回Unit或者值

提示:函数式风格——尽量编写有返回值的函数; 尽量简短(函数体不使用{…})

3.1.2. 映射式定义

一种特殊的定义:映射式定义(直接相当于数学中的映射关系);

其实也可以看成是没有参数的函数,返回一个匿名函数;调用的时候是调用这个返回的匿名函数。

例子1:

def f:Int=>Double = { // 请看做 def f: (Int=>Double) = {...}
case 1 =&gt; 0.1case 2 =&gt; 0.2case _ =&gt; 0.0

}

f(1) // 0.1

f(3) // 0.0

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

例子2:

def m:Option[User]=>User = {
case Some(x) =&gt; xcase None =&gt; null

}

m(o).getOrElse(“none…”)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

例子3:(多->1)

def m:(Int,Int)=>Int = +

m(2,3) // 5

  • 1
  • 2
  • 3
  • 4

例子4:

def m:Int=>Int = 30+  // 相当于30+,如果唯一的""在最后,可以省略

m(5) // 35

  • 1
  • 2
  • 3
  • 4

3.1.3. 特殊函数名 + - * /

方法名可以是*:

def (x:Int, y:Int) = { xy }

*(10,20) // = 200

1+2 // 相当于1.+(2)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

定义一元操作符(置前)可用unary_。

注:unary:一元的,单一元素的,单一构成的 。发音:【`ju: ne ri】

-2,相当于:(2).unary_- // -2

+2,相当于:(2).unary_+ // 2

!true, 相当于:(true).unary_! // false

~0,相当于 (0).unary_~ // -1

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.1.4. 缺省参数、命名参数

注意:从Scala2.8开始支持。

请对比以下两种写法的可读性:

一般函数调用 命名函数调用
sendEmail("jon.pretty@example.com",List("recipient@example.com"),“Test email”,b,Nil, Nil, Nil) sendEmail( body = b, subject = “Test email”, to = List("recipient@example.com"), from = "jon.pretty@example.com", attachments = List(file1, file2) )

定义和使用:

def join(a:List[String], s:String="-") = { a.mkString(s) }

join(List(“a”,“b”,“c”)) // a-b-c

join(List(“a”,“b”,“c”), “:”) // a

Scala 快查手册相关推荐

  1. 《Linux命令速查手册》笔记

    写在前面:我看过很多技术相关的书籍,但是很少有全部看完的.大部分都是看了一小半,甚至是一大半.但是印象中,没有哪一本是全部读完的,哪怕的通读. 不过这本<Linux命令速查手册>,我倒是很 ...

  2. zxl CMD 命令速查手册

    CMD 命令速查手册 1. 摘要 2. 显示或修改文件扩展名关联 3. 显示或更改文件属性. 4. 设置或清除扩展式 CTRL+C 检查. 5. 设置 boot.ini 文件的属性以便控制启动加载. ...

  3. CMD命令速查手册 cmd命令大全

    CMD命令速查手册 cmd命令大全 cmd命令详解如果你想了解本页面是怎么来的 请下载批处理代码 到--其他\CMD命令速查手册 运行 就可以得到本页面 CMD 命令速查手册 雪深 - 2009-03 ...

  4. MATLAB函数速查手册(修订版) - 电子书下载(高清版PDF格式+EPUB格式)

    MATLAB函数速查手册(修订版)-邓薇 在线阅读                   百度网盘下载(a8m9) 书名:MATLAB函数速查手册(修订版) 作者:邓薇 格式:EPUB, HTMLZ, ...

  5. 生信人的自我修养:Linux 命令速查手册

    标题:生信人的自我修养:Linux 命令速查手册 目标:致力于为生信人打造一个完整的 Linux 命令速查手册 作者:简佐义(jianzuoyi@qq.com) 版本:1.0 日期:2020-11-2 ...

  6. Linux命令行命令速查手册(整理)

    Linux命令行命令速查手册 [作者原创]Jan [ls] 2017-02-09 ls命令会列出当前所在目录的内容 2017-02-09 ls music 注 想要看看music目录中有什么内容,那么 ...

  7. CMD 命令速查手册

    CMD 命令速查手册 ASSOC 显示或修改文件扩展名关联. ATTRIB 显示或更改文件属性. BREAK 设置或清除扩展式 CTRL+C 检查. BOOTCFG 设置 boot.ini 文件的属性 ...

  8. c语言各常用函数,c语言常用函数速查手册 pdf

    C语言常用函数速查手册是一本非常全面系统的讲述了学习c语言相关的常用函数,包括常见的350多个常用函数,且每一个常用函数后面都会有一个经典的示例帮助更快掌握C语言,欢迎下载. 图书简介: 为了方便查找 ...

  9. [WebDev]Web 开发与设计师速查手册大全

    Cheat Sheet 一词在中文中并没有很贴切的对译,大概是考试作弊条一类的东西,这要求 Cheat Sheet 必须短小精悍又覆盖广泛,作为 Web 开发与设计师,免不了在工作时查询大量资料,某个 ...

  10. 最全pandas函数用法速查手册(高清版)

    Pandas 是 Python 的核心数据分析支持库,拥有快速.灵活.明确的数据结构,旨在简单.直观.快速地处理关系型.标记型数据,是一款强大.灵活的开源数据分析工具. 但是pandas的知识点很多, ...

最新文章

  1. tomcat远程调试
  2. 【Shell脚本】逐行处理文本文件
  3. python正则匹配ip地址_Python正则表达式匹配ip地址实例
  4. Android 五大存储方式具体解释
  5. 织梦 mail.class.php,详解织梦模板DEDECMS核心类TypeLink.class.php功能分析
  6. 安装mysql 5.6.24给linux,Red Hat Enterprise Linux 5 64位安装Mysql5.6.24(DB5.6.24.rpm for rhel5 x86)...
  7. MyBatis常用配置解析-mapper标签
  8. pandas处理大数据的一些小技巧
  9. nginx上传目录配置,禁止执行权限
  10. 将语音识别准确率提升40% 他是当下最受比尔·盖茨器重的中国人
  11. matlab接触封装,MATLAB如何解除封装
  12. 成功解决3dmax中,旋转时透视图可以看穿物体
  13. 什么软件能测试gps高度,‎App Store: GPS海拔测量仪-实时高度测量海拔表
  14. 《操作系统真象还原》第九章 ---- 终进入线程动斧开刀 豁然开朗拨云见日 还需解决同步机制才能长舒气
  15. 使用js编写用户注册(简洁版)
  16. excel转pdf的在线免费转换技巧,超实用
  17. Sizzle选择器揭秘--Sizzle选择器
  18. Matlab中hold函数使用
  19. 人不可貌象、小三不可斗量
  20. python语言应用智慧树答案_智慧树Python语言应用答案全部

热门文章

  1. MyExcel 2.1.4 版本发布,多项功能增强
  2. 360抢票 网站维护中 你的登录被踢了!
  3. 什么是CDN加速服务器?
  4. Google谷歌的未来 野心:2017 Google I/O 大会 全程回顾
  5. 软文管家发布平台_企业软文如何做好
  6. number1(python)
  7. 阿里巴巴图库下载的在线图标显示为方框的问题
  8. win10去除快捷小箭头_【批处理】win 去除桌面图标小箭头
  9. ***技巧总结(zz)
  10. 达梦数据库DM8启动与关闭