本系列博客以《Programming in Scala 2nd Edition》为主,围绕其中的代码片段进行学习和分析。

  本文主要梳理Chapter2和Chapter3中涉及到的主要概念。

一、变量定义时的val和var

  

  在Scala中定义一个变量可以使用两种关键字var和val。其中val类似于Java中的final类型变量,值不可变。var定义的变量值是可变的。

1、val变量

当给一个val变量重新赋值时就会出现如下提示:

  

2、var变量

如果确实需要更改某个变量的值,可以使用var。下面这个就不会出现错误提示

  

二、函数的定义

1、简单定义

  定义一个求两个变量最大值的函数

def max(x: Int, y: Int): Int = {
if (x > y) x
else y
}

  

  接下来我们仔细分析一下这个定义的语法结构,定义一个函数以def开头,接下来是函数名称max,传入参数列表(x: Int, y: Int),然后是返回类型Int,用等号与函数体相连。

  

2、简化

  其实这个函数可以简写成以下形式,scala会根据函数后面的表达式,自动推算出返回类型。下面这个if表达式中,返回类型应该是if的两个分支结果类型的最近一个公共父类。

def max2(x: Int, y: Int) = if (x > y) x else y

  

3、无返回值函数

  前面两种定义的函数,都会有一个返回值,接下来定义一个没有返回值的函数,

def greet() = println("Hello, world!")

  

三、循环结构

  在任何编程语言中循环结构都是最重要,使用最频繁的一种定义结构。接下来简要介绍一下其中的while,foreach,以及for循环。

1、while循环

  定义如下,

var i = 0
while (i < args.length) {
println(args(i)
i += 1
}

运行该方法时传入参数为Scala is fun,结果如下,

  

2、foreach循环

  上面的功能可以使用foreach简写,下面代码中调用args数组的foreach方法,注意该方法中接收的参数是一个函数。由于args是一个String类型的数组,Scala编译器会自动推算出传入的函数中arg的类型也是String,所以不需要写成arg: String的形式。

args.foreach(arg => println(arg))

结果如下:

  

  在Scala中,当一个函数表达式中只有一个语句,并且只接收一个参数时,可以进一步简写。最终这个循环简写成

args.foreach(pringln)

  总结一下,三种foreach循环的写法如下:

args.foreach(arg => println(arg))
args.foreach((arg: String ) => println(arg))
args.foreach(println)

  在第二种写法中表示了Scala中怎样传递一个函数表达式当参数,这个函数表达式参数没有函数名,参数和函数体用=>分隔,参数需要用圆括号包围。

  

3、for循环

for (arg <- args)
println(arg)

输入参数for arg in args运行代码,结果如下:

  

四、构造一个对象

  和Java中一样,在Scala中也可以使用new关键字生成一个类的对象,并且在构造时,可以往构造函数中传入一些参数。

1、简单对象

  比如下面这个表达式中,会生成一个java.matm.BigInteger类型的对象,并且值为12345。

val big = new java.math.BigInteger("12345")

2、数组对象

  定义一个数组对象,并赋值,最后循环打印出该数组中的内容。0 to 2表示生成0, 1, 2的序列值。

val greetStrings = new Array[String](3)
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!\n" for (i <- 0 to 2)
print(greetStrings(i))

  严格意义上,在Scala中是没有操作符这个说法的,比如说+,-,*,/,其实都是方法调用的简写形式。

1 + 2其实是Int型对象1调用一个函数名为+的函数,传入的对象是Int型变量2。所以完整的写法是(1).+(2)

  

  在上面的例子中,还可以看到,和Java中不同的是,数组下标是用圆括号传递的。比如greetStrings(1)其实是greetStrings.apply(1)的简写形式。调用的是数组的apply方法,传入参数是数组下标值,数组的apply方法的作用是获取传入下标处的元素值。在Scala中任何对象后面直接用圆括号接收的值,其实都是该对象调用apply方法的简写形式。

  但是如果对象后面直接跟圆括号并传入参数,最后用=进行赋值,实际上调用的是该对象的update方法,比如

greetStrings(0) = "Hello"

编译器会将其解析成

greetStrings.update(0, "Hello")

  另外,数组的初始也可以在定义时一并完成,比如

val numNames = Array("zero", "one", "two")

上面的定义会自动确定Array中的元素类型为String。并且这个写法其实也是调用了Array的apply方法。所以等价于下面这种写法

val numNames2 = Array.apply("zero", "one", "two")

  联想一下第一节中的内容,你认为一个Array对象到底是可变的呢,还是不可变的?Array对象是可变的(mutable)对象。因为,虽然在一个Array被构造出来之后,其长度是不可变的,但是其中的元素还是可以被重新赋值的。

五、List的用法

  如果想要实现一个不可变(immutable)的对象列表,可以使用马上要介绍到的scala.List。在Java中使用的List是java.util.List类型,这个List是mutable类型的。

val oneTwoThree = List(1, 2, 3)

1、List中的方法

(1):::

在List中,有一个方法:::用于连接两个List,比如,

val oneTwo = List(1, 2)

val threeFour = List(3, 4)

val oneTwoThreeFour = oneTwo ::: threeFour

println(oneTwo +” and “+ threeFour +” were not mutated.”)

println(“Thus, “+ oneTwoThreeFour +” is a new list.”)

(2)::

这个方法用于在List前面新增加一个元素

val twoThree = List(2, 3)
val oneTwoThree = 1 :: twoThree
println(oneTwoThree)

::左边的对象当作一个元素追加到::右边的List元素之前,上面这个表达式的最终结果是

  

但是如果

val oneTwoThreeFour = oneTwo :: threeFour

结果是新的List中第一个元素为oneTwo这个List,如下:

  

(3)Nil

  其实这个不是一个方法,Nil的作用是生成一个空的list。结合::方法和Nil也可以构造并初始化一个List

val oneTwoThree = 1 :: 2 :: 3 :: Nil

  表示的是将3追加到空list的前面,形成一个新的List,然后追加2,又生成一个新的List,最后追加1。那么最终结果是,

  

  如果最后不写Nil,是无法将这些值转化成一个List的。

  

  

  最后列举出List常见的用法:

用法 作用及返回值
List() 或 Nil 生成一个空的List
List(“Cool, “Tools”, “rule”) 构造一个List对象并初始化
val thrill = “will” :: “fill” :: “until” :: Nil 构造一个List对象并初始化
List(“a”, “b”) ::: List(“c”, “d”) 将两个List中的元素展开并拼接生成一个新的List
thrill(2) 获取thrill中的第2个元素,即rule
thrill.count(s => s.length ==4) count方法传入一个方法,统计出thrill中元素字符长度为4的所有元素个数,结果为2
thrill.drop(2) 返回一个去除thrill中前2个元素的list,结果为List(“until”)
thrill.dropRight(2) 去除右边2个元素,结果为List(“will”)
thrill.exists(s => s == “untill”) exists接收一个方法,判断thrill List中是否包含”untill”元素,结果为true
thrill.filter(s => s.length == 4) 筛选出thrill中长度为4的元素,结果为List(“will”, “fill”)
thrill.forall(s => s.endsWith(“l”)) 判断是否所有元素都以l结尾,只有当所有元素都满足该条件时才返回true。可以类比exists,exists是或的关系,forall是与的关系
thrill.foreach(s => print(s)) 使用传入的方法循环处理thrill中的每一个元素。这里是打印
thrill.foreach(print) 上面的简写形式
thrill.head 获取thrill中的第一个元素,结果为”will”
thrill.init 返回一个去除最后一个元素的list,结果为List(“will”, “fill”)
thrill.isEmpty 判断thrill是否为空,结果为false
thrill.length 获取thrill中的元素个数,结果为3
thrill.map(s => s + “y”) 返回一个新的list,新的list中每一个元素为thrill中元素依次调用传入方法的结果,结果为List(“willy”, “filly”, “untily”)
thrill.mkString(“,”) 将thrill中的元素用逗号拼接,返回一个String,结果为”will,fill,untill”
thrill.remove(s => s.length == 4) 返回一个新的list,这个list中不包含thrill中长度为4的元素,结果为List(“until”)
thrill.reverse 翻转thrill中的元素,返回List(“until”, “fill”, “will”)
thrill.sort((s, t) => s.charAt(0).toLower < t.charAt(0).toLower) sort方法接收一个方法当参数,这个传入的方法中需要接收两个参数值,根据这两个元素的第0个字符编码大小排列,结果为true表示排在前面。结果是List(“fill”, “untill”, “will”)
thrill.tail 去掉thrill的第一个元素,得到一个新的list。结果是List(“fill”, “until”)

六、Tuple的用法

  Tuple也是immutable的,当需要一个方法返回多个对象时,可以考虑使用Tuple,对比Java中,为了返回多个对象,一般会构造一个JavaBean对象,将需要返回的值都由这个JavaBean来中转。Scala中的Tuple就可以理解成一个JavaBean对象,只不过这个对象不需要你去创建,并且不需要指定JavaBean的类名,如果在下面这个Tuple的初始化前加一个new ClassName的话,几乎就是构造一个JavaBean对象的代码了。

val pair = (99, "Luftballons")
print(pair._1)
print(pair._2)

  获取Tuple中的元素,可以用_1的形式来实现。List中是用list(1)的形式来访问指定元素,那么Tuple中为什么不行呢?这是因为List中的元素一般都是相同的类型,List的apply方法知道返回的对象什么类型的。而Tuple中的元素一般为不同类型,_1和_2的类型可能就完全不同。

七、Set和Map的用法

1、Set

  在Scala中Set的类继承关系如下图:

  

  最上面有三个名字相同的trait,Scala中的trait类似于Java中的interface,这三个名称相同的trait可以用包名加以区分。

  创建一个Set对象可以使用以下方法,类似于初始化一个Array对象或者List对象,

var jetSet = Set("Boeing", "Airbus")
jetSet += "Lear"
println(jetSet.contains("Cessna"))

初始化jetSet对象包含两个String后,第二行代码等价于jetSet = jetSet + "Lear"。对mutable类型的Set,是将Lear追加到当前对象的最后。而对于imutable类型的Set,则是将Lear追加到最后,并返回一个新的Set对象。

执行结果如下图,

  

  如果需要指定初始化一个mutable类型的Set,在最开始使用import命令导入对应的Set即可,

import scala.collection.mutable.Set
val movieSet = Set("Hitch", "Poltergeist")
movieSet += "Shrek"
println(movieSet)

2、Map

  首先看一下Map的类结构图,这个结构图和Set很相似。

  

  初始化一个Map对象,使用以下代码

import scala.collection.mutable.Map
val treasureMap = Map[Int, String]()
treasureMap += (1 -> "Go to island.")
treasureMap += (2 -> "Find big X on ground.")
treasureMap += (3 -> "Dig.")
println(treasureMap(2))

结果如下

  

  

  在初始化之前使用import导入需要使用的类型。key和value之间用”->”连接。使用+=将新的键值对导入Map对象中。

  也可以在声明时直接初始化

val romanNumeral = Map(1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV", 5 -> "V")
println(romanNumeral(4))

Programming In Scala笔记-第二、三章的更多相关文章

  1. Programming In Scala笔记-第十七章、Scala中的集合类型

    本章主要介绍Scala中的集合类型,主要包括:Array, ListBuffer, Arraybuffer, Set, Map和Tuple. 一.序列 序列类型的对象中包含多个按顺序排列好的元素,可以 ...

  2. Programming In Scala笔记-第十一章、Scala中的类继承关系

    本章主要从整体层面了解Scala中的类层级关系. 一.Scala的类层级 在Java中Object类是所有类的最终父类,其他所有类都直接或间接的继承了Object类.在Scala中所有类的最终父类为A ...

  3. Programming In Scala笔记-第六章、函数式对象

    这一章主要是以定义和完善一个有理数类Rational为线索,分析和介绍有关类定义,构造函数,方法重写,变量定义和私有化,以及对操作符的定义等. 一.Rational类定义和构造函数 1.定义一个空类 ...

  4. Programming In Scala笔记-第五章、Scala中的变量类型和操作

    这一章的一些基础性的东西,主要包括Scala中的基本变量类型,以及相关的一些操作符. 一.简单类型 下表中列出Scala语言中的基本类型,以及其字节长度,其中Byte, Short, Int, Lon ...

  5. Programming In Scala笔记-第七章、Scala中的控制结构

    所谓的内建控制结构是指编程语言中可以使用的一些代码控制语法,如Scala中的if, while, for, try, match, 以及函数调用等.需要注意的是,Scala几乎所有的内建控制结构都会返 ...

  6. Programming In Scala笔记-第四章、类和对象

    类似于Java,Scala中也有类和对象的概念. 一.类.属性和方法 1.类 类是对一类事物的抽象,当一个类被定义后,就可以以该定义为模板,定义该类的一系列对象.比如说有以下一个模板 人类: 有姓名: ...

  7. 2018-11-27 中文代码示例之Programming in Scala笔记第七八章

    续前文: 中文代码示例之Programming in Scala学习笔记第二三章 中文代码示例之Programming in Scala笔记第四五六章. 同样仅节选有意思的例程部分作演示之用. 源文档 ...

  8. Android群英传笔记——第三章:Android控件架构与自定义控件讲解

    Android群英传笔记--第三章:Android控件架构与自定义控件讲解 真的很久没有更新博客了,三四天了吧,搬家干嘛的,心累,事件又很紧,抽时间把第三章大致的看完了,当然,我还是有一点View的基 ...

  9. 2018-12-09 疑似bug_中文代码示例之Programming in Scala笔记第九十章

    续前文: 中文代码示例之Programming in Scala笔记第七八章 源文档库: program-in-chinese/Programming_in_Scala_study_notes_zh ...

随机推荐

  1. HTML的各种基本标签

      一 .head中的各种标签                1.       <!DOCTYPE html><html>文档类型声明   声明当前文件是一个HTML5文件文档 ...

  2. springaop——AspectJ不可不知的细节

    springaop简介 springaop是spring对AOP技术的具体实现,它是spring框架的核心技术.springaop底层使用JDK动态代理或CGLIB动态代理技术实现. 应用场景: 在多 ...

  3. further occurrences of HTTP header parsing errors will be logged at DEBUG level.错误

    今天进行项目测试的时候出现了further occurrences of HTTP header parsing errors will be logged at DEBUG level.错误,查了半 ...

  4. python的布尔类型与流程控制

    布尔类型其实可以算是一种特殊的数字,下面是 help() 函数得到的帮助信息:  bool 首先,请注意这一行:

  5. 微信小程序开发小记

    年前的时候,因为公司开发小程序的人员不够,临时参与了一个项目中几个小模块的开发,这里做个简单的小记录,眼过千篇不若手过一遍,希望将来如果要用到时不至于大脑空白! 开发工具:wechat_devtool ...

  6. “百度杯”CTF比赛 九月场_SQLi

    题目在i春秋ctf大本营 看网页源码提示: 这边是个大坑,访问login.php发现根本不存在注入点,看了wp才知道注入点在l0gin.php 尝试order by语句,发现3的时候页面发生变化,说明 ...

  7. [Luogu 2062]分队问题

    Description 给定n个选手,将他们分成若干只队伍.其中第i个选手要求自己所属的队伍的人数大等于a[i]人. 在满足所有选手的要求的前提下,最大化队伍的总数. 注:每个选手属于且仅属于一支队伍 ...

  8. ●BZOJ 2393 Cirno的完美算数教室

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2393 题解: 容斥原理,暴力搜索,剪枝...和 [Scoi2010 幸运数字] 一样的(只是 ...

  9. python dataframe数据条件筛选

    一般情况下我们从一堆数据中选择我们获取想要的数据会通过一下方式: (1)创建链表或数组: (2)用for 循环遍历所有数据,将想要的存入链表或数组. 但是python中我们不需要这么做,我们可以用Pa ...

  10. python txt文件的写入和读取

    1.文件的打开 使用open () 函数 打开文件.他有两个参数,文件路径或文件名和文件的打开方式. "r" 只读模式,不能编辑和删除文件内容. "w" 写入模 ...