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

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

一、变量定义时的val和var

  

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

1、val变量

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

  

2、var变量

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

  

二、函数的定义

1、简单定义

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

  1. def max(x: Int, y: Int): Int = {
  2. if (x > y) x
  3. else y
  4. }

  

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

  

2、简化

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

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

  

3、无返回值函数

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

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

  

三、循环结构

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

1、while循环

  定义如下,

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

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

  

2、foreach循环

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

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

结果如下:

  

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

  1. args.foreach(pringln)

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

  1. args.foreach(arg => println(arg))
  2. args.foreach((arg: String ) => println(arg))
  3. args.foreach(println)

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

  

3、for循环

  1. for (arg <- args)
  2. println(arg)

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

  

四、构造一个对象

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

1、简单对象

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

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

2、数组对象

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

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

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

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

  

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

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

  1. greetStrings(0) = "Hello"

编译器会将其解析成

  1. greetStrings.update(0, "Hello")

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

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

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

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

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

五、List的用法

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

  1. 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前面新增加一个元素

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

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

  

但是如果

  1. val oneTwoThreeFour = oneTwo :: threeFour

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

  

(3)Nil

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

  1. 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对象的代码了。

  1. val pair = (99, "Luftballons")
  2. print(pair._1)
  3. 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对象,

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

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

执行结果如下图,

  

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

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

2、Map

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

  

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

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

结果如下

  

  

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

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

  1. val romanNumeral = Map(1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV", 5 -> "V")
  2. 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. hue上配置HA的hdfs文件(注意,HA集群必须这样来配置才能访问hdfs文件系统)

    按照正常方式配置,发现无论如何也访问不了hdfs文件系统,因为我们是HA的集群,所以不能按照如下配置 将其改为 除此之外,还需要配置hdfs文件的 接着要去hadoop的目录下启动httpfs.sh ...

  2. TP-LINK | TL-WR842N设置无线转有线

    首先点击右上角的"高级设置". 点击左侧的"无线设置"栏,点击"WDS无线桥接",然后一步步设置可以使路由器连接到当前的一个无线网络. 然后 ...

  3. webstorm中github的配置

    1.申请一个github账号,我这里的操作是已经有了账号的情况之下进行的. 打开webstorm,File-->settings,弹出settings框,输入git,得到以下界面,输入githu ...

  4. Now trying to drop the old temporary tablespace, the session hangs.

    1.描述 问题描述:删除临时表空间时,会话Hangs挂起 SQL> drop tablespace TEMP_B including contents and datafiles; 2.故障诊断 ...

  5. 从零开始搭建支持http2的web服务

    前段时间开始,公司各项业务开始陆续接入http2,关于http2的优点与所适用的场景网上有很多的文档可以查阅,这里我主要是总结分享一下如何从0到1搭建http2服务. 这里先说明一下,要完成http2 ...

  6. servlet之重写

    package app02a;import java.io.IOException;import java.io.PrintWriter;import java.util.ArrayList;impo ...

  7. codefroces 946F Fibonacci String Subsequences

    Description定义$F(x)$为$F(x−1)$与$F(x−2)$的连接(其中$F(0)="0"$,$F(1)="1"$)给出一个长度为$n$的$01$ ...

  8. Go学习——new()和 make()的区别详解(转载)

    这篇文章主要介绍了Go语言中new()和 make()的区别详解,本文讲解了new 的主要特性.make 的主要特性,并对它们的区别做了总结,需要的朋友可以参考下 概述 Go 语言中的 new 和 m ...

  9. ●SPOJ LCS2Longest Common Substring II

    题链: http://www.spoj.com/problems/LCS2/题解: 后缀自动机. 对第一个串建立后缀自动机, 然后把后面的每个串分别与该串的自动机去匹配,求出相应的数组val*[s]: ...

  10. Codeforces Round#409/VK-Cup 2017 Round2

    来自FallDream的博客,未经允许,请勿转载,谢谢. 和ditoly组队打VK-Cup,起了个名字叫Vegetable Chicken(意思显然),然后昨天我做AB他切C 很不幸的是.....我写 ...