scala学习笔记
第2章 变量和数据类型
基本数据
- scala的核心数据为四种 :字面量、值、变量、类型
- 值使用val来定义、变量使用var来定义
val x: Int =20
var x: Int =30
,定义值和变量 - 在scala中,值优先于变量,这是因为可以为源代码带来稳定性和可预测性。
数值型类型
- Byte
- Short
- Int
- Long
- Float
- Double
scala不允许从较高等级类型自动转换到较低等级类型,但是可以使用toType方法手动完成类型间的转换,所有数值型都有这样一个方法。例如:toInt\toString\toDouble、、
字符串
注:双引号为字符串String,单引号为字符Char
与数值型类似,String类型也支持使用数学运算符。
"string"+"abc"、"string2"*2
等等可以使用三重引号创建多行String。多行字符串是字面量,所以如果其中使用了反斜杠,不要认为是一个特殊的转义字符。
字符串内插
- 要在一个String中加入值或变量,更直接的一种方法是利用字符串内插。
- println(s"Pi,using 355/113,is about $approx");
- 如果引用中有非字字符,或者如果引用与周围文本无法区分,就要使用大括号:
s"How do you like them ${item}s?"
正则表达式
- 使用.r函数将普通的字符串转换为正则表达式,
val pattern = "scala".r
,和java类似。 - 使用findFirstIn 函数来查找第一个匹配的表达式,pattern findFirstIn input
- 使用findAllIn函数来查找所有匹配上的表达式,但是好像并不会打印出来,此时使用mkString(",")函数来链接两个字符串
println((pattern findAllIn input).mkString(","))
- 类似的,使用replaceFirst 和replaceAll ,
input replaceAll ("a.*","a")
等掌握熟练后回过头来继续研究。
核心非数值类型
类型名 | 描述 | 可否实例化 |
---|---|---|
Any | Scala中所有类型的根 | 否 |
AnyVal | 所有值类型的根 | 否 |
AnyRef | 所有引用(非值)类型的根 | 否 |
Nothing | 所有类型的子类 | 否 |
Null | 所有指示null值的AnyRef类型的子类 | 否 |
Char | Unicode字符 | 是 |
Boolean | true 或 false | 是 |
String | 字符串 | 是 |
Unit(void) | 指示没有值 | 否 |
- 注意&&和& 的使用的区别,一个是在if判断条件等中使用,另外一个是逻辑运算。
- -Unit类型相当于java、C中的void返回类型,即为空类型。一般用来定义函数和表达式。
类型操作
操作名 | 示例 | 描述 |
---|---|---|
asInstanceOf | 5.asInstanceOf[Long] | 将一个值转换为指定类型的值。如果这个值与新类型不兼容,会导致一个错误 |
getClass | (7.0/5).getClass | 返回一个值的类型 |
isInstanceOf | (5.0).isInstanceOf[Float] | 判断一个值的类型是否为指定的类型,如果是则返回true |
hashCode | "A".hashCode | 返回这个值的散列码 |
to |
20.toByte ,47.toFloat | 转换函数,可以将一个值转换为一个兼容的值 |
toString | (3.0/4.0).toString | 将值显示为字符串类型 |
- 注意:避免使用asInstanceOf,如果值无法转换为所请求的类型,asInstanceOf操作会导致一个错误,为了避免这个操作带来的运行的错误,应当尽可能使用toType完成类型转换。
元组
- 元组是一个包含两个或多个值的有序容器,所有的这些值可以有不同的类型。相当于关系表中的一个样本,data.frame中的一个元素。(不同于R语言中的向量和数组,因为R中的向量和数组必须是全部是相同的类型。)
val info = (5,"Korben",true)
- 可以根据元素的索引访问元组中的单个元素,从1开始。
info._2
- 要创建一个大小为2的元组,另一种候选形式是利用关系操作符 (->) 。这是一种表示元组中键和键值的流行的快捷方式:
val red = "red" -> 15 ,直接创建一个键和键值对,相当于二元元组
第3章 表达式和条件式
表达式块
- 可以使用大括号结合多个表达式创建一个表达式块。表达式有自己的作用域,可以包含表达式块中的局部值和变量。块中的最后一个表达式将作为整个表达式块的返回值。
val x=5*20;val amount=x+10
、{val a=1;{val b=a*2;{val c=b+4;c}}}
语句
- 语句就是不返回值的表达式。语句的返回类型为Unit,这种类型指示没有值。scala编程中常用的语句包括println()调用以及值和变量定义。
if...else表达式块
val max = if(x>y) x else y
- 类似于java和C中的if...else表达式。
匹配表达式
- 匹配表达式类似C和java中的switch语句,首先会计算一个输入项,并执行第一个匹配模式,然后返回它的值。只有0个或1个模式可以匹配。
- scala的匹配表达式非常灵活,允许匹配各种各样的项,如类型、正则表达式、数值范围和数据结构内容
语法:使用匹配表达式
match {
case =>
[case ...]
}
val x=10;val y=20;
val max = x>y match {
case true => x
case true => y
}
val status=500 ;
val message = status match {
case 200 => "ok"
case 400 => {println("ERROR - we called the service incorrectly")
println("error")
}
case 500 => {println("ERROR - the service encountered an error")
println("error")}
}
语法:模式替换式
值绑定 case |.. =>
val day ="mon"
val kind =day match {
case "mon"|"tue"|"wed" |"thu" |"fri" => "weekday"
case "sat"|"sun" => "weekday"
}
- 如果匹配的表达式没有为输入表达式提供一个模式的匹配,则scala会报错。
用通用模式匹配
语法:值绑定模式 case
val message ="Ok"
val status =message match {
case "Ok" => 200
case other => {println(s"Couldn't parse $other")
-1}
}
通配符绑定
通配符是一个下划线字符,相当于一个匿名占位符,最后将在运行时替换为一个表达式的值。
语法: 通配符模式 case _ =>
- 不能在箭头右侧访问通配符。
val message ="abc"
val status =message match {
case "Ok" => 200
case _ => {println(s"Couldn't parse $message")
-1
}
}
用模式哨卫匹配
- 模式哨兵向值绑定模式增加一个if表达式,从而可以为匹配表达式增加条件逻辑。使用哨兵模式时,只有当表达式返回true时模式才匹配。
语法:模式哨兵 case
if => - 与常规的if表达式不同,这里的if 表达式不需要在其分布表达式的两边加小括号。常规的if表达式需要小括号来简化整个命令的解析,并从条件表达式中区分出布尔表达式。不过如果愿意,也可以 添加小括号。
val res : String =null
res match {
case s if s!=null => println(s"Received '$s'")
case s => println("Error! Received a null res")
}
用模式变量匹配类型
- 利用匹配表达式完成模式匹配还有一种方法,即匹配输入表达式的类型。如果匹配,模式变量可以把输入值转换为一个不同的值,然后可以在case块中使用这个新值和类型。
语法 : 指定模式变量 case
: =>
val x: Int =12180;
val y: Any =x ;
y match {
case x:String => s"'x'"
case x:Double => f"$x%.2f"
case x:Float => f"$x%.2f"
case x:Long => s"${x}l"
case x:Int => s"${x}i"}
循环
- for循环
语法:用基本for循环迭代处理 for(
注意: <-符号之间必须没有空格,否则会报错
yield 关键字是可选的。如果表达式中指定了这个关键字,调用的所有表达式的返回值将作为一个集合返回。如果没有指定这个关键字,但是制定了表达式,将会调用这个表达式,但是不能访问它的返回值。
for(x <- 1 to 7) {println(s"Day $x:")}
val xxx=for(x <- 1 to 7)yield {s"Day $x:"}
for(day <- xxx) println(day)//此时不需要初始化值day,直接使用for循环
- 加上yield关键字后,循环中的数据被存到了一个集合中。
2.迭代器哨兵
- 类似匹配表达式中的模式哨兵,迭代器哨兵也称为过滤器,可以为迭代器增加一个if表达式。
语法: 迭代器哨兵 for(
val threes =for (i <- 1 to 20 if i % 3==0 )yield i
//定义迭代器哨兵
val quote ="a,b,c"
for(t<- quote.split(",");if t!=null ;if t.size>0) println(t)// 和java一样,集合的大小使用.size,分裂一个字符串使用.split
- 嵌套迭代器
- 嵌套迭代器是增加到一个for循环的额外的迭代器,迭代总数随迭代器的个数倍增。之所以被称为嵌套迭代器,是因为把它们增加到同一个循环中与写为单独的嵌套循环有相同的效果。
//双层for循环
for(x<- 1 to 2 ;y<- 1 to 3){//两个条件之间的空格还是必须要的
print(s"$x,$y")}
- while和Do/While循环
- 除了for循环外,scala还支持while循环和do/while循环,不过,在scala中没有for循环常用,因为它们不是表达式,不能用来获取值。
语法: while循环 while(
//while循环
var x=10; while (x>0) x-=1
//do while循环
val x=0
do println(s"Here I am,x=$x") while (x>0)
//死循环
var x=0;
do {println(s"here I am ,x=$x");
x+=1;
}while (x>0)
- 具体的差别和java中的定义是一样的,关键是do while循环不管条件是否成立,会先运行一次。
第4章 函数
语法 : 定义无输入的函数 def
=
def hi ="hi"
语法: 定义函数时指定返回类型 def
: = 定义函数 def
( : [,...]): =
def multiplier (x:Int,y:Int):Int ={x*y}
multiplier(6,7)
- 这些函数的函数体基本上由表达式或表达式块组成,在这里最后一行将成为表达式的返回值,相应的也是函数的返回值。
- 也可以显示的使用return来直接返回一个函数的返回值。
过程
- 过程是没有返回值的函数,以一个语句结尾的函数是一个过程。如果有一个简单的函数,没有显示的返回类型,而且最后一个是一个语句,scala编辑器就会推到出这个函数的返回类型是Unit,这表示没有值。
用空括号定义函数
语法: 用空括号定义函数 def
def hi() : String ="hi"
hi()
使用表达式块调用函数
语法: 用表达式块调用函数
递归函数
- 例子
def power(x:Int,n:Int):Long={
if(n>=1) x*power(x,n-1)
else 1
}
嵌套函数
- 函数是命令的参数化表达式,而表达式块是可以嵌套的,所以函数本身也是可以嵌套的。可以在函数中定义另一个内部函数,这个内部函数只能在该函数中使用。
def max(a:Int,b:Int,c:Int)={
def max(x:Int,y:Int)=if(x>y) x else y
max(a,max(b,c))
} //这里的嵌套函数与外部函数同名,但是由于它们的参数不同,所有它们之间不会发生冲突
方法和操作符
- 方法是类中定义的一个函数,这个类的所有实例都会有这个方法。scala 中调用方法的标准做法是使用中缀点记法,方法名前面有实例和一个点分隔作为前缀。
语法 : 用中缀点记法调用方法
[(parameters)]
val s="vacation.jpg"
val isJEPG=s.endWith(".jpg")
// compare、round、floor函数
val d =65.642
d.round //四舍五入
d.floor //下取整
d.compare(18) //大小比较
- 对于单个参数的方法,直接使用操作符记法来调用方法
语法: 用操作符记法调用方法
val d=12;
d compare 18;
d + 27;
//string.substring(2,4)
第5章 首类函数
- 函数式编程的关键是函数应当是首类的。“首类”表示函数不仅能得到声明和调用,还可以作为一个数据类型用在这个语言的任何地方。
- 如果一个函数接受其他函数作为参数,或者使用函数作为返回值,这就成为高阶函数。例如:map和reduce
函数类型和值
- 函数的类型是其输入类型和返回值类型的一个简单组合,由一个箭头从输入类型指向输出类型
- 将函数赋给一个值中进行存储
语法:函数类型 ([
,...])=>
def double(x:Int):Int =x*2
double(5)
val myDouble:(Int) => Int =double
myDouble(5)
val myDoubleCopy =myDouble
myDoubleCopy(5)
- myDouble就是一个值,只不过与其他值不一样,它可以调用double
- 使用通配符为函数赋值
语法 val
= _
def double(x:Int):Int =x*2
val myDouble =double _
val amount=myDouble(20)
- 将函数赋值,不仅需要将函数名赋值,而且需要将函数的参数类型同时进行赋值
高阶函数
- 高阶函数也是函数,它包含一个函数类型的值作为输入参数或返回值。
//例1
println(apply(layout,10))
def apply(f:Int => String,v:Int) =f(v)
def layout[A](x:A):String ={"["+x.toString+"]"}//泛型函数,输入值可以为任意的类型,输出值为String类型
//例2
def safeStringOp(s:String,f:String => String) ={
if(s!=null) f(s) else s
}
def reverser(s:String) = s.reverse
safeStringOp(null,reverser)
safeStringOp("Ready",reverser)
匿名函数(函数字面量)
语法:编写函数字面量 ([
val doubler =(x:Int) => x*2
val doubled = doubler(22)
- 匿名函数没有固定返回值的类型
//做一个比较
def max(a:Int,b:Int) ={if(a>b) a else b }//一般函数定义
val maximize :(Int,Int) => Int =max //将max函数赋值给maximize值
val maximize =(a:Int,b:Int) => if(a>b) a else b //直接使用匿名函数来定义
maximize(48,65)
占位符语法
- 占位符语法是匿名函数的一种缩写形式,将命名函数替换为通配符下划线。可以在以下情况使用:
- 函数的显示类型在字面量之外定义
- 函数最多只使用一次
def combination(x:Int,y:Int,f:(Int,Int)=>Int)=f(x,y)
combination(23,12,_*_)
- 这里使用了两个占位符,这确实会使语法更为抽象,它们会按照位置替换输入参数,如果使用一个额外的通配符,将会导致错误,以为占位符的个数必须与输入参数个数一致。
def tripleOp(a:Int ,b:Int,c:Int,f:(Int,Int,Int)=>Int )=f(a,b,c)
tripleOp(23,92,14,_*_+_)
//使用泛型函数来定义
def trip[A,B] (a:A,b:A,c:A,f:(A,A,A)=> B)=f(a,b,c)
println(trip[Int,Int](12,22,33,_+_+_))
- 占位符语法在处理数据结构和集合时尤有帮助。
部分应用函数和柯里化
- 柯里化是对包含多个参数的函数分步计算,类似于数学分析中的分部积分的思想
def factor(x:Int)(y:Int) ==y %x==0
val isEven =factor(2) _
val z =isEven(32)
第6章 常用集合
- 集合框架提供了一些数据结构来收集给定类型的一个或多个值,如:数组、列表、映射、集合树
列表、集合映射
- List类型是一个不可变的单链表。可作为一个函数调用List来创建一个列表,并以逗号分隔参数的形式传入列表的内容:
val numbers =List(32,95,24,21,17)
numbers.size //求集合的基数大小
- 集合中的元素必须是统一类型的,可以初始化集合元素的类型
val x =List[Int] (1,2,3)
println(x.size)
- 集合中的head和tail方法可以查看集合的首部几个元素和尾部几个元素
- 集合中索引下标使用小括号来进行索引,和matlab中索引的方法是类似的,从0开始哦!!!
x.head
x.tail //注意不能加上小括号
x(1)
x(3)//此时会造成数组越界
- 使用List.fill来创建一个指定重复数量的元素列表:
object Test {
def main(args: Array[String]) {
val site = List.fill(3)("Runoob") // 重复 Runoob 3次
println( "site : " + site )
val num = List.fill(10)(2) // 重复元素 2, 10 次
println( "num : " + num )
}
}
- head函数为返回一个列表中的首元素、tail函数为返回列表中其余的元素
- 集合中的for循环有两种,一种是使用for(i <- 1 to x.size) ,另外一种是使用for (i <- x)
- scala中大量使用了高阶函数来完成迭代、映射、归约。
val colors=List("red","green","blue")
colors.foreach((c:String)=> println(c))
val sizes =colors.map((c:String)=>c.length)//c.length为字符串的长度,c.size一样
val numbers =List(32,95,24,21,17)
val total =numbers.reduce((a:Int,b:Int)=> a+b)
println(sizes)
println(total)
//foreach取一个函数,对列表中的每一项分别调用这个函数
//map()取一个函数,将一个列表元素转换为另一个值或类型
//reduce()取一个函数,将两个列表元素结合为一个元素
//foreach、map、reduce均为高阶函数,输入的参数为函数
- set是一个不可变的无序集合,只包含不重复的唯一元素,不过其工作与List类似。//类似于java中的HashSet集合
val unique=Set(10,20,30,20,20,10)
val sum =unique.reduce((a:Int,b:Int)=> a+b)
println(sum)
- List和Set的区别在List中允许出现重复的元素,而Set中不允许出现重复的元素。
- Map是一个不可变的键值库,在Map中,值按照一个给定的唯一键存储,可以使用这个键来获取相应的值。键和键值都可以类型参数化,像创建从整数到字符串的映射一样,也可以很容的创建从字符串到整数的映射。
- 创建Map时,指定键-值为元组。可以使用关系型操作符来指定键和值元组。
val colorMap =Map("red"-> 1, "blue"-> 2, "green" -> 3)
val redRGB =colorMap("red")
val cyanRGB =colorMap("green") | colorMap("blue")
val hasWhite =colorMap.contains("white")//考察键值库中是否存在键为white的键
for(i <- colorMap) {println(i)}//使用for循环,和List、Set一样的操作
- List里面有什么??
- 可以在集合中存储任何类型的值,而不只是目前为止我们所用的数字和字符串。
- 可以把一个列表分解为表头(head)和表尾(tail),表头即表的第一项,表尾即表中的其余项。
- List是一个不可变的递归数据结构,所以列表中的每一项都有自己的表头和越来越短的表尾。
三种方法来遍历List表
- 使用isEmpty方法
val numbers =List(32,95,24,21,17,28,39,40,23,34)
var i=numbers //定义一个变量来进行遍历
while(!i.isEmpty){print(i.head+",");i=i.tail}
- 使用递归来进行遍历
val numbers =List(32,95,24,21,17,28,39,40,23,34)
def visit(i:List[Int]):Unit = {if(i.size)>0 {print(i.head+",");visit(i.tail)}}
visit(numbers)
- 调用Nil实例作为终结点,所有列表都有一个Nil实例作为终结点,所以迭代器可以通过比较当前元素和Nil来检查是否达到列表末尾
val numbers =List(32,95,24,21,17,28,39,40,23,34)
var i=numbers //定义一个变量来进行遍历
while(i!=Nil){print(i.head+",");i=i.tail}
注:Nil实际上是List[Nothing] 的一个单例实例。
val l:List[Int] =List()
l == Nil
val m:List[String] =List("a")
m.head
m.tail==Nil
Cons操作符
- 使用Nil作为基础,并使用右结合的cons操作符::绑定元素,就可以构建一个列表,而不必使用传统的List(...) 格式
val numbers=1::2::3::Nil
for(i <- numbers) println(i)
列表算术算
var x1 =1::2::3::Nil //使用符号::
println(x1)
var x2 = List(1,2):::List(2,3)//合并两个列表使用符号:::
for(i <- x2) println(i)
println(List(1,2)++Set(3,4))//合并一个列表List和Set使用符号++
println(List(2,3,34,55,2,3).distinct)//取出List中的唯一的元素
println(List(2,3,34,55,2,3) drop 2)//删除List中列表的前两个元素
println(List(2,3,234,323,42,21) filter (_>18))//筛选出List列表中大于18的元素
println(List(1,2,3,4,5) partition (_<3)) //根据一个true/false 函数的结果,将元素分组为由两个列表构成的一个元组
println(List(List(1,2),List(3,4)).flatten)//将两个列表扁平化变为一个列表
println(List(1,2,3).reverse)//将列表进行翻转
println(Set(1,2,3) slice (1,2))//从列表的第一个索引直到第二个索引,但不包括第二个索引
List("apple","to") sortBy (_.size)//按给定函数返回的值对列表进行排序
List("apple","to").sorted
List(2,3,5,7,11,13) take 3 //从列表中抽取前n个元素
映射列表
- 映射方法是指一个函数,将它应用于列表的每一个成员,再把结果收集到一个新列表。
- 以下为高阶函数
//列表映射操作
List(0,1,0) collect {case 1=> "ok"} //使用一个偏函数转换各元素,保留可应用的元素
List("milk,tea","abc,dfsdf") flatMap (.split(",")) //使用一个给定函数转换各个元素,将结果列表“扁平化”到这个列表中
List("milk","tea") map (_.toUpperCase) //使用给定函数转换各个元素
List("apple","to") sortBy (_.size)//按给定函数返回的值对列表进行排序
println(List(2,3,234,323,42,21) filter (_>18))//筛选出List列表中大于18的元素
println(List(1,2,3,4,5) partition (_<3)) //根据一个true/false 函数的结果,将元素分组为由两个列表构成的一个元组
归约列表
- 归约列表是处理集合的一个常见操作。将列表收缩为单个值。scala的集合操作支持数学归约操作(例如,查找一个列表的总和)和逻辑归约操作(例如,确定一个列表是否包含某个给定的元素)
- 它们还支持通用的高阶操作。
List(41,59,26).max //查找列表中的最大值
List(41,59,26).min //查找最小值
List(4,5,67).product //将列表中的元素乘积
List(11.3,23.5,7.2).sum //将列表中的元素相加
//下面的操作将列表归约为一个布尔值。
List(24,39,18) contains 23
List(0,3,4) endsWith List(3,4)
List(5,6,7) exists (_<18)
List(0,4,3) startsWith List(0)
- 自己定义一个归约的操作函数
def contains (x:Int,l:List[Int] ) :Boolean ={
var a =false
for(i <- l) {if(!a) a=(i==x) }
a
}
- 将以上函数推演归纳泛化到一般的情况
def reduceOp [A,B] (l:List[A],start:B)(f:(B,A) => B):B ={
var a =start
for(i<- l) a = f(a,i)
a
}
println(reduceOp[Int,Int](List(1,2,3),0)((x:Int,y:Int) => x+y ))
println(reduceOp[Int,Int](List(1,2,3),0)(su))
def su (x:Int,y:Int):Int ={x+y}
转换集合
- 现在来考虑列表、集、映射之间的转换
操作名 | 示例 | 描述 |
---|---|---|
mkString | List(24,99,104).mkString(",") | 使用给定分隔符讲一个集合呈现为String |
toBuffer | List('f','t').toBuffer | 将一个不可变的集合转换为一个可变的集合 |
toList | Map("a"-> 1,"b"-> 2).toList | 将一个集合转换为一个List |
toMap | Set(1->true,3-> true).toMap | 将一个2元元组的集合转换为一个Map |
toSet | List(2,5,5,3,2).toSet | 将一个集合转换为一个Set |
toString | List(2,5,5,3,2).toString | 将一个集合呈现为一个String,包括集合的类型 |
-
List(1,2,3,4,5,3,4,2).toSet.size
和List(1,2,34,5,3,4,2).distinct.size
均可以计算两个列表中唯一元素的个数 -
List(2,5,5,3,2).toBuffer.append(10)
可以使用java中的Buffer类进行可变集合中元素的增加
第7章 更多集合
可变集合
- 要修改集合,最直接的方法是利用一个可变的集合类型。见下表,列出了对应表准的不可变List、Map和Set类型的可变集合的可变集合类型。
不可变类型 | 可变类型 |
---|---|
collection.immutable.List | collection.mutable.Buffer |
collection.immutable.Set | collection.mutable.Set |
collection.immutable.Map | collection.mutable.Map |
- 尽管collection.immutable package 会自动增加到scala的当前命令空间,但不会增加collection.mutable package 。创建可变的集合时,要确保包含类型的完整包名。
val nums=collection.mutable.Buffer(1)//创建一个可变的集合Buffer,其中只有元素1
for (i <- 2 to 10) nums+=i //集合中+=为添加元素
//或者使用nums.append(i)
println(nums.getClass)
println(nums)
//从一个空集合开始添加元素
val nums1=collection.mutable.Buffer[Int]() //由于没有默认值,我们必须使用类型参数[Int],否则编译失败
for(i <- 1 to 10) nums1+=i
println(nums1)
- 构建映射和集合的过程也类似。创建一个空的集合时候,只需要为新的集合指定类型的参数,或者为一个新映射指定键和键值的类型参数
- 集合和映射类似,可以使用toList和toSet方法把这些可变的集合转换为不可变的集合类型。
从不可变集合创建可变集合
- 除了直接创建可变的集合,还可以从不可变的集合转换为可变的集合。如果开始有一个不可变的集合,希望修改这个集合,或者希望写List而不是 collection.mutable.Buffer()
- 不可变的集合List,Map,Set都可以使用toBuffer方法转换为collection.mutable.Buffer 类型。对于列表,显然这很自然。
//将一个不可变的映射转换为可变的映射,然后再转换回不可变的映射
val m=Map("AAPL" -> 579,"MSFT" -> 40)
val b=m.toBuffer
val n=b.toMap
val n1=b.toList
val n2=b.toSet
val b.append("AAPL" -> 579)
val n3=b.toSet
- Buffer 类型是一个很好的通用的可变集合,与List类似,不过还可以增加、删除和替换内容。由于它支持转换方法,而且相应的不可变类型提供了toBuffer 方法,使得Buffer类型成为处理可变数据的一个很有用的机制
使用集合构建器
- Builder 是Buffer的一个简化形式,仅限于生成指定的集合类型,而且支持追加操作。
数组
Array是一个固定的可变索引集合。这不是正式意义上的集合,因为它不在scala colloctions包里面。
Array类型实际上只是java数组的一个包装器,另外还提供了一个高级特性,成为隐含类,是的它可以像序列一个样使用。
scala提供Array类型来保证与jvm和java代码的兼容性,另外用来作为索引集合的后备库,这使得数组很有用。
数组不同于List、set,它是一个可变的集合类,可以对数组中的元素进行赋值操作。
val colors=Array("red","green","blue")
colors(0)="purple"
Seq和序列
- Seq是所有序列的根类型,包括类似List的链表和类似Vector的索引列表。
- 作为一个跟类型,Seq本身不能实例化,但是可以调用Seq创建List,这是创建List的一个快捷方式:
val inks =Seq('C','M','Y','K')
类型名 | 描述 |
---|---|
Seq | 所有序列的根类型,List()的快捷方式 |
IndexedSeq | 索引序列的根类型,Vector()的快捷方式 |
Range | 整数范围。动态生成数据 |
LinearSeq | 线性(链表)序列的根类型 |
List | 元素的单链表 |
Queue | 先进先出列表 |
Stack | 后进先出列表 |
Stream | 懒列表。访问元素时候才增加相应元素 |
String | 字符集合 |
Option
- Scala Option(选项)类型用来表示一个值是可选的(有值或无值)。
- Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None 。
// 虽然 Scala 可以不定义变量的类型,不过为了清楚些,我还是
// 把他显示的定义上了
val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1")
val value2: Option[String] = myMap.get("key2")
println(value1) // Some("value1")
println(value2) // None
- isEmpty() 方法
- 可以使用 isEmpty() 方法来检测元组中的元素是否为 None,实例如下:
object Test {
def main(args: Array[String]) {
val a:Option[Int] = Some(5)
val b:Option[Int] = None
println("a.isEmpty: " + a.isEmpty )
println("b.isEmpty: " + b.isEmpty )
}
}
第8章 类
类
- 类是面向对象语言的核心构建,这是数据结构与函数的组合。用值和变量定义的类可以根据需要实例化多次,每一个实例都可以用自己的输入数据初始化。
- 类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板
class User
val u=new User
//新建一个类
- 现在会有一个类,会看到类名和一个十六进制串。
import java.io._
class Point(xc:Int,yc:Int){
var x:Int=xc
var y:Int=yc
def move(dx:Int,dy:Int){
x=x+dx
y=y+dy
println("x的坐标点:"+x);
println("y的坐标点:"+y);
}
}
object Test{
def main(args:Arrary[String]){
val pt =new Point(10,20);
pt.move(10,10)
}
}
//scala中的类不声明为public,一个scala源文件中可以由多个类
//以上实例的类定义了两个变量x和y,一个方法move,方法没有返回值
//scala的类定义可以有参数,称为类参数,如上面的xc和yc,类参数在整个类中都可以访问
//接着我们可以使用new来实例化类,并访问类中的方法和变量。
scala继承
- scala继承一个基类跟java很相似,但我们需要注意以下几点:
- 重写一个非抽象方法必须使用override修饰符
- 只有主构造函数才可以往基类的构造函数里写参数
- 在子类中重写超类的抽象方法时,你不需要使用override关键字
- 一个类可以使用extends关键字扩展最多一个其他类,另外可以用override关键字覆盖所继承方法的行为。类中的字段和方法可以用this关键字访问,父类中的字段和方法可以用super关键字访问。如果一个方法需要访问其父类中的相应方法,super关键字尤其有用。
class A {
def hi ="Hello from A"
override def toString =getClass.getName
}
class B extends A
class C extends B {override def hi = "hi C -> "+super.hi}
val hiA =new A().hi
val hiB =new B().hi
val hiC =new C().hi
import java.io._
class Point(val xc: Int, val yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("x 的坐标点 : " + x);
println ("y 的坐标点 : " + y);
}
}
class Location( val xc: Int, val yc: Int,
val zc :Int) extends Point(xc, yc){
var z: Int = zc
//参数重写中不需要加上override
def move(dx: Int, dy: Int, dz: Int) {
x = x + dx
y = y + dy
z = z + dz
println ("x 的坐标点 : " + x);
println ("y 的坐标点 : " + y);
println ("z 的坐标点 : " + z);
}
}
object Test {
def main(args: Array[String]) {
val loc = new Location(10, 20, 15);
// 移到一个新的位置
loc.move(10, 10, 5);
}
}
- 只有重写方法函数需要加上修饰符override
- scala使用extends关键字来继承一个类。实例中Location类继承了Point类。Point称为父类,Location称为子类。
- 继承会继承父类的所有属性和方法,scala只允许继承一个父类
scala单例对象
- 在scala中,没有static这个东西,但是它也为我们提供了单例模式的实现方法,那就是使用关键字object。scala中使用单例模式时,除了定义的类之外,还要定义一个同名的object对象,它和类的区别就是,object对象不能带参数。
- 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以相互访问其私有成员。
import java.io._
class Point(val xc: Int, val yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
}
}
object Test {
def main(args: Array[String]) {
val point = new Point(10, 20)
printPoint
def printPoint{
println ("x 的坐标点 : " + point.x);
println ("y 的坐标点 : " + point.y);
}
}
}
定义类
- 类是对一个类型的定义,其中包含有核心类型和其他的字段(值和变量)。类还包含方法,也就是处理包含字段的一些函数,另外还包含嵌套定义。
class [extends ] [{fields,methods,and classes}]
类似表达式和函数,类可以相互嵌套。嵌套类除了可以访问自己的字段和方法,还可以访问其父类的字段和方法。实例就是一个内存分配,提供了类字段的存储空间。这个动作称为实例化。可以使用new关键字按类名实例化一个类,可以加小括号,也可以没有。
更有用的是类可以由参数,这是初始化类所用的字段和方法的输入值,或者甚至可以作为类的字段。类参数是一个用逗号分隔的名字和类型的列表,形式与函数输入参数相同。
//例子
class car(val make:String,var reserved :Boolean){
def reserve (r:Boolean) :Unit ={reserve =r}
}
val t =new car ("Toyota",false)
t.reserve(true)
println(s"My ${t.time} is now reserve?${t.reserve}")
val t2 =new car(reserved=false ,make ="Tsela")
class lotus(val color:String,reserved :Boolean) extends car ("lotus",reserved)
可以用标准中缀点记法访问一个类的字段和方法,实例与字段之间用一个点号分隔。
如果一个类扩展了其他需要参数的类,则需要确保类定义中包含这些参数。extends关键字后面的类应当根据需要有自己的一组输入参数。
可以为参数定义默认值。
class car (val make:String,var reserved :Boolean =true ,val year :Int =2015)
{
override def toString =s"$yead $make ,reserved =$reserved "
}
val a =new car("Acura")
val l =new car("Lexus",year =2010)
val p =new car(reserved =false ,make ="Porsche")
重载方法
- 重载方法是为调用者提供更多选择的一种策略。一个类可能有两个或者多个名字和返回值相同的方法,但是输入参数的设置不同。通过多个实现重载一个方法名,调用一个特定名字的方法时可以有多重选择。
class Printer(msg:String){
def print(s:String) :Unit =println(s"$msg:$s")
def print(l:Seq[String]):Unit =print(l.mkString(","))
}
new Printer("Today's Reprot").print("Foggy"::"Rainy"::"Hot"::Nil)
- 重载是一个有用的特性,不过很多scala开发人员更愿意使用默认值参数而不是重载。可以在方法中为参数提供默认值,而不是提供两个方法(一个方法有参数,另一个方法无参数,无参数的方法会调用另一个有参数的方法并提供默认值),这样可以减少编写不必要的代码。
apply方法
- apply的方法有时是指作为一个默认或一个注入方法,可以直接调用而不需要方法名。apply方法实际上是一个快捷方式,可以使用小括号出发功能而不需要方法名。
class Multiplier (factor:Int){
def apply(input:Int) =input* factor
}
val tripleMe =new Multiplier(3)
val tripled =tripleMe.apply(3)
- 将一个方法作为默认方法有一个潜在的缺点,这可能会让代码看起来有些奇怪。访问默认方法应当很自然,比如列表的存取方法。类似列表的存取方法,只有在合理的情况下才应当使用apply方法。
包装
- 包就是scala中的代码组织系统。利用包,可以按照目录使用点分隔路径来组织scala代码。如果在scala源文件前最前面使用package关键字,则声明这个文件中的所有类都将包含在这个包中。
语法:为scala文件定义包
package
- scala采用java标准来为包命名,包以组织或企业域的逆串开头,然后加上其他名构成路径。
- scala源文件要存储在与包相匹配的目录中。
com.netflix.utilities 包应该存放在 com/netflix/utilities/DateUtilities.scala
//scala编译器会把生成的.class文件存储在与包匹配的目录结构中
访问包装类
- 要访问其他包中的类,一种更高效的方法是将它们导入到当前命名空间。这样不需要包前缀也可以访问这个类。导入一个类时需要使用import关键字,后面是完整包和类名。
语法:导入一个包装类
import .
- 与java不同,代码的任何地方都可以使用import语句
- scala还支持用下划线操作符导入一个包的全部内容。导入后,这个包中的每一个类都会增加到命名空间。
import collection.mutable._
val b =new ArrayBuffer[String]
b.append("a","b","c")
- 从一个包导入所有类和子包可能有一个缺点。如果所导入的包中有一个类与命名空间中的一个类同名,那么就无法访问命名空间中国已有的那个类。
导入包组
- 除了导入整个包,另一种做法是使用导入组。利用这个特性,可以列出一组导入的类名,而不是一个完整的包。
语法:使用导入组
import .{[,...]}
import collection.mutable.{Queue,ArrayBuffer}
val q =new Queue[Int]
val b =new ArraryBuffer[String]
导入包别名
语法:使用导入别名
import .{ => }
私密性控制
- protected 定义字段,不允许外部类访问。不过,该字段仍可以由子类访问
- private ,仅限定义这个字段或方法的类可以访问。类之外的所有其他代码甚至子类都不允许访问这个字段或者方法。
class User {protected val passwd =util.Random.nextString(10)}
附录 代码
import java.io._
object HelloWorld {
def main(args:Array[String]) :Unit ={//Unit类相当于void返回值,为空
println("Hello World");
var x :String ="abc";
println(x);
println(x.getClass);
val y ="xcvxcv".r;
println(y.getClass());
println("A".hashCode);//返回一个值的散列码
println((30/40).toString);
println((20.5).isInstanceOf[Float]);
val info = (5,"Korben",true)
println(info);
println(tran(20.5))
println()
println(round(3.7245));
print("\n")
val flag:Boolean =false;
val result:Boolean =(flag==false)
println(flag,result)
//重写上面的Boolean类型的定义
val flag1 =false;
val result1 =(flag1==false)
println(flag1,result1)
println();
println(128.toChar);
println(128.toString);
println(128.toDouble);
println(128.toString.toDouble.toInt);
//正则表达式,需要加强学习
val zzbds="\\d{3}-\\d{3}-\\d{4}".r;
val input="Frank,123 Main,925-555-1943,95122";
println(zzbds findFirstIn input);
println((zzbds findAllIn input).mkString(","));
val status=600 ;
val message = status match {
case 200 => "ok"
case 400 => {println("ERROR - we called the service incorrectly")
println("error")
}
case 500 => {println("ERROR - the service encountered an error")
println("error")}
case other => {println("could not find"); -1}
}
println(message)
val res : String =null
res match {
case s if s!=null => println(s"Received '$s'")
case s => println("Error! Received a null res")//此处匹配s为匹配任意值
}
val xxx = for(x <- 1 to 7;if x % 2 !=0;if x >5 ) yield {s"Day $x:"}
// for (day <- xxx)println(day+",")
println(xxx)
//定义迭代器哨兵
val quote ="a,b,c"
for(t<- quote.split(",");if t!=null ;if t.size>0) println(t)
//多层迭代器
for(x<- 1 to 2 ;y<- 1 to 3){//两个条件之间的分号还是必须要的
println(s"$x,$y")}
println()
println(f1(""))
println("f3函数的值为:"+f3(2.333))
println("f33函数的值为:"+f33(0))
println();
/* for(i <- 1 to 100)
{
if(i %5 ==0) println(i)
else print(i+",")
}
*/
for(i <- 1 to 100){
if(i %3 ==0 && i % 5!=0) { print("type,")}
else if(i %3 !=0 && i %5 ==0 ) {print ("safe,");println()}
else if(i %3 ==0 && i %5 ==0 ){ print ("typesafe,");println();}
else print(i+",")}
for(i <- 1 to 100){
var y = i match{
case x if(x%3==0 && x%5!=0) => "type"
case x if (x%3!=0 && x%5==0) => "safe"
case x if (x%3==0 && x%5==0) => "typesafe"
case other => i
}
if(i %5==0) println(y+",") else print(y+",")}//println函数是打印完之后再换行
println(safeTrim(" ab c "));
println(safeTrim(""));
println()
println(identity("adsfdasf").getClass)
println(identity(123434).getClass)
val d=2134;
println(d.compare(2135))
println(d compare 2135)
// println(area(10))
//mm(134134343444)
println(pow(2,30))
val yuanzu=(1234,"33",true)//元组
// if (yuanzu._1.isInstanceOf[Int]) {println(yuanzu._1)}
val yy =(yuanzu._1,yuanzu._2,yuanzu._3,yuanzu._1.toString,yuanzu._2.toString,yuanzu._3.toString)
println(yy)
// yuanzu.productIterator.foreach{k => {if(k.isInstanceOf[Int]) println(k)}}//元组遍历
println(apply(layout,10))//复合函数
//使用匿名函数
val maxxx = (a:Int,b:Int) => if(a>b) a else b
println(maxxx(2,3))
/*
object HelloWorld {
def oncePerSecond(callback: () => Unit) { while (true) { callback(); Thread sleep 1000 } }
def main(args: Array[String]) { oncePerSecond(() => println("time flies like an arrow...")) }
}
*/
//高阶函数
def safeStringOp(s:String,f:String => String) ={
if(s!=null) f(s) else s
}//定义复合函数
def reverser(s:String) = s.reverse//定义函数
println(safeStringOp(null,reverser))
println(safeStringOp("Ready",reverser))
val ff = (x:Int,y:Int) => x*y
def combination(x:Int,y:Int,f:(Int,Int)=> Int):Int= f(x,y)
println(combination(23,12,ff))
println(combination(23,12,(x:Int,y:Int) => x*y))
def tripleOp(a:Int ,b:Int,c:Int,f:(Int,Int,Int)=>Int )=a+b*f(a,b,c)-c
println(tripleOp(23,92,14,_*_+_))
//使用泛型函数来定义
def trip[A,B] (a:A,b:A,c:A,f:(A,A,A)=> B)=f(a,b,c)
println(trip[Int,Int](12,22,33,_+_+_))
println()
val maxium = (x:Int,y:Int) => if(x>y) x else y;
println("the max number is : "+maxium(2,3))
def yu (x:Int,y:Int,z:Int,maxium:(Int,Int)=> Int) = maxium(maxium(x,y),z)
println(yu(1,2,3,maxium))
println()
println(util.Random.nextInt)//返回一个随机的整数
println(util.Random.nextDouble)//返回一个随机的双精度类型
println(util.Random.nextLong)//返回一个随机的长整型数
println()
//val x =util.Random.nextInt
//val y =util.Random.nextInt
def twomax (x:Int,y:Int,f:(Int,Int)=> Int) = f(x,y);
println(twomax(util.Random.nextInt(),util.Random.nextInt(),maxium))
def fzero[A](x:A)(f:A => Unit):A={f(x);x}
val zz = fzero[String]("abc")(println)
def square(m:Double):Double ={m*m}
//val sq : Double => Double = square
val sq = (x:Double) => x*x
println(sq(10.1))
def conditional[A] (x:A,p: A=> Boolean,q : A => Boolean )= {
if(p(x) && !q(x)) print("type,");
if(!p(x) && q(x)) println("safe")
if(p(x) && q(x)) println("typesafe")
if(!p(x) && !q(x)) print(x+",")
}
val px =(x:Int) => x %3==0
val qx =(x:Int) => x%5==0
for(i <- 1 to 100){conditional[Int](i,px,qx) }
/*def px (x:Int):Boolean = {
if(x>0) true else false
}*/
//val px = (x:Int) => if(x>0) true else false //匿名函数
//val fx = (x:Int) => x+1//匿名函数
/*def fx (x:Int):Int ={
x+1
}*/
// println(conditional[Int](10,px,fx))
val p_x = List(1,2,3)
//for (i <- p_x) {println(i)}
//for (i <- 1 to p_x.size) {println(i)}
val colors=List("red","green","blue","red","blue")
colors.foreach((c:String)=> println(c.length))
val sizes =colors.map((c:String)=>c.length)
val numbers =Set(32,95,24,21,17,17)
val numbers2 =List(32,95,24,21,17,17,28,29,30,31)
println("numbers的集合基数为:"+numbers.size)
println("numbers2的集合基数为:"+numbers2.size)
val total =numbers.reduce((a:Int,b:Int)=> a+b)
println(numbers+"Set为")
println(numbers2+"List为")
println(sizes)
println("集合中元素的和为:"+total)
/*Map类,键与键值
val colorMap =Map("red"-> 1, "blue"-> 2, "green" -> 3, "green" -> 4)//如果这样会忽略green指向3的键和键值
val redRGB =colorMap("red")
val cyanRGB =colorMap("green") | colorMap("blue")
val hasWhite =colorMap.contains("white")
println(cyanRGB)
println(hasWhite)
for(i <- colorMap) {println(i)}
*/
var i =numbers2//将i设置为numbers的一个动态变量
println(i.head)
println()
println(i.tail)
println()
while(! i.isEmpty){println(i.head+",");i=i.tail}
println()
println()
def visit(i:List[Int]):Unit ={
if(i.size>0) {print(i.head+","); visit(i.tail) }
if(i.size==1) {print(i.head); visit(i.tail) }
}//递归遍历一个List
visit(numbers2)
println()
val num3 =List(32,95,24,21,17,28,39,40,23,34)
var i2=num3 //定义一个变量来进行遍历
while(i2!=Nil){print(i2.head+",");i2=i2.tail}
println()
println()
val n3=1::2::3::Nil
println(n3)
for(i <- n3) println(i)
def factors (x:Int):List[Int] ={
val y = for(i <- 2 to x-1 if(x%i==0))yield{ i } //返回值为vector类型
return y.toList
}
println(factors(16))
println(List(15,11,9).flatMap(factors))
def fir[A] (items:List[A],count:Int):List[A]={
val y=for(i <- 0 to count-1)yield {
items(i)
}
return y.toList
}//输出一个列表的前count个数据
println(fir(List("a","b","c"),2))
def longstring (x:List[String]):List[String] ={//返回字符串长度最长的字符串
var max=0
for(i <- 0 to x.size-1) {
if(x(i).length>= max) max=x(i).length
}//找出最大值
val index =for(i <- 0 to x.size-1 if(x(i).length == max))yield{
i
}//找出最大值的下标
val S = for(i <- index.toList)yield{
x(i)
}//提取出最大值
return S.toList
}
println(longstring(List("a","abc","ac","adsf","adsfa","rer","addfd")))
def revse[A](x:List[A]):List[A]={
val y = for(i <- 0 to x.size-1 )yield {
x(x.size-1-i)
}
return y.toList
}
println(revse(List("a","abc","ac","adsf","adsfa","rer","addfd")))
//def pati (x:List[String]):(List[String],List[String])
def pati (x:String) :Boolean={
var y=true
for(i <- 0 to x.size-1 if(x(i)!=x(x.size-1-i)))
{
y=false
}
return y
}
println(List("a","abc","ac","adsf","adsfa","rer","addfd").partition(pati))
///////////////可变集合////////////////////
println("可变集合:")
val nums=collection.mutable.Buffer(1)//创建一个可变的集合Buffer,其中只有元素1
for (i <- 2 to 10) nums.append(i)
println(nums.getClass)
println(nums)
//从一个空集合开始添加元素
val nums1=collection.mutable.Buffer[Int]() //由于没有默认值,我们必须使用类型参数[Int],否则编译失败
for(i <- 1 to 10) nums1+=i
println(nums1)
val x1 = Array[Int](10,2,3,10)
val x2=x1.toBuffer
for(i <- x1) println(i)
println(x2.getClass)
println(x2)
x2.append(200)
println(x2)
val a:Option[Int] = Some(5)
val b:Option[Int] = None
println("a.isEmpty: " + a.isEmpty )
println("b.isEmpty: " + b.isEmpty )
val xyz = List(1,2,3).toBuffer
println(xyz.getClass)
println(xyz)
//面向对象编程
val pt =new Point(10,20);
pt.move(10,10)
val loc =new Location(10,20,15);
loc.move(10,10,5)
println();
val users =List(new User("Shoto"),new User("Art3mis"),new User("abc"));
println(users)
var x12 =new User("xycz");
println(x12)
println(x12.greet)//面向对象编程
val sizess=users.map(_.name.size);//.name方法返回一个函数的名字
val names=users.map(_.name);//.name方法返回一个函数的名字
println(sizess)
println(names)
val hiA =new A().hi
val hiB =new B().hi
val hiC =new C().hi
println(hiA)
println(hiB)
println(hiC)
println()
val x3 = List(10,20,30).reduce(plus)
println(x3)
val x4 =List(10,20,30,20,10).foldLeft(false)((x:Boolean,y:Int) => if(x) x else y==30)//判断一个列表中是否包含某一个元素
println(x4)
class Printer(msg:String){
def print(s:String) :Unit =println(s"$msg:$s")
def print(l:Seq[String]):Unit =print(l.mkString(","))
}
new Printer("Today's Reprot").print("Foggy"::"Rainy"::"Hot"::Nil)
class multiplier(factor:Int){
def apply(input:Int)=input*factor
}
println()
val trip0 =new multiplier(3)//新建一个对象
val trip1=trip0.apply(10)//执行方法
val trip2=trip0(10)//隐藏了方法
println(trip1)
println(trip2)
import java.util.Date//导入java包
val dd =new Date
println(dd)
}
//////////////////////主函数结束////////////////////////////////////
def plus (x:Int,y:Int):Int ={
return x+y
}
def apply(f:Int => String,v:Int) =f(v)
def layout[A](x:A):String ={"["+x.toString+"]"}
//定义函数////////////////////////////////////////////////
def identity[A](a: A):A=a //该函数设置无论输入什么类型的参数都返回输入值
def safeTrim(s:String):String ={ //该函数功能为去掉字符串两端的空格
if(s=="") return null //判断字符串是否是为空的,如果为空则返回null
s.trim()
}
def ff (xx: String): String ={
if(xx !="") return xx else return "n/a";
}
def f1 (xx: String): String ={
var y = xx match {
case "" => "n/a";
case other => xx;
}
return y
}
def f3 (q : Double ): String = {
if(q>0) return "greater" ;
else if(q==0) return "same" ;
else return "less";
}
def f33(q: Double ): String ={
var y:String=""
y = q match {
case x1 if(x1>0 || x1==0) => "greater"
case x3 if (x3<0) => "less"
}
return y
}
//实现将摄氏温度转换为华氏温度
def tran (xx:Double ):Int= {
var yy :Double =0;
yy=(xx*9/5)+32;
return yy.toInt;
}
//定义一个幂指数函数
def power(x:Int,n:Int):Long={
if(n>=1) x*power(x,n-1)
else 1
}
//实现保留两位小数的四舍五入
def round (a :Double ): String ={
var x:Double =0;
var x1:Int =0;
var x2:Int =0;
var x3:Int =0;
var x4:Int =0;
x=a*1000;
x1=x.toInt/1000;
x2=x.toInt%(x1*1000)/100;
x3=x.toInt%(x1*1000+x2*100)/10;
x4=x.toInt%(x1*1000+x2*100+x3*10);
if(x4>=5){
x3=x3+1}
var u:String =( x1+0.1*x2+0.01*x3).toString;
return s"You owe $$$u";
}
//第4章练习
def area(x:Double): Double ={
return 3.14*x*x
}
def mm(x:Long):Unit={
val day = x / (3600*24*1000);
val hour = x%(3600*24*1000)/(1000*3600);
val min = x%(3600*24*1000)%(1000*3600)/(60*1000)
val sec = x%(3600*24*1000)%(1000*3600)%(60*1000)/1000
println(s"day=${day},hour=${hour},min=${min},sec=${sec}")
}
def pow(x:Int,y:Int):Long ={ //定义一个幂指数函数
var pro =1;
for (i <- 1 to y)
{
pro=pro*x
}
return pro
}
}//静态类结束
class Point(xc:Int,yc:Int){
var x:Int=xc
var y:Int=yc
def move(dx:Int,dy:Int){
x=x+dx
y=y+dy
println("x的坐标点:"+x);
println("y的坐标点:"+y);
}
}
class Location( val xc :Int, val yc :Int,val zc:Int) extends Point(xc,yc){
var z:Int =zc
def move (dx:Int,dy:Int,dz:Int){
x=x+dx;
y=y+dy;
z=z+dz;
println("x的坐标点:"+x);
println("y的坐标点:"+y);
println("z的坐标点:"+z);
}
}
class User(val name:String){
def greet:String =s"Hello from $name"
override def toString =s"User($name)"
}
class A {
def hi ="Hello from A"
override def toString =getClass.getName
}
class B extends A
class C extends B {override def hi = "hi C -> "+super.hi}