Scala的数据类型、对象、控制结构、函数和闭包---Scala学习笔记(2)

来源:互联网 发布:java bigdecimal 加1 编辑:程序博客网 时间:2024/06/05 07:43

继续上次的学习
1.数据类型

  • Scala的常用的基本

数据类型:

Byte 8 位有符号补码整数(-2 ~2 -1)
Short 16 位有符号补码整数(-2 ~2 -1)
Int 32 位有符号补码整数(-2 ~2 -1)
Long 64 位有符号补码整数(-2 ~2 -1)
Char 16 位无符号Unicode字符(0~2 -1)
String 字符序列
Float 32 位 IEEE754 单精度浮点数
Double 64 位 IEEE754 单精度浮点数
Boolean true 或 false
—–《Scala编程》p69

String类型是Java.lang包下,而其他数据类型都源自于Scala自己的包下,Scala鼓励将int写成Int,但是数据类型的范围和Java是一样的。
其中浮点数文本例如:

val Double=1e1

编译器默认认为是双精度浮点型,如果要定义单精度浮点型:

val float=1e1f

后面加F即可
关于字符文本:赋值可以使用Unicode八进制,十六进制,或者’\u’+八进制或十六进制:

val ch='\u0041'

另外,可以在定义变量名时使用Unicode编码:

val hol\u0041=1

就可以定义变量’hola’了
关于字符串:
原先用两个双引号环绕字符获得字符串,但是如此以来加入转义字符就很麻烦

val escapes = "\\\"\'"

所以Scala定义同一行里的三个引号代表原始字符串,里面可以加入任何东西:

val escapes = """\\\"\'"""

操作符和方法:
Scala里的任何操作符都是方法的调用,这个在上一次的笔记中已经提到。同时,Scala还可以将不带参数的方法调用中的括号去除:println() 可以直接写成println

  • 对象相等性
    和Java的对象比较不同,Scala的’==’运算符经过改进,直接返回内容比较的结果,而不是对象地址的比较:
scala>List(1,2,3)==List(1,2,3)res1:Boolean = true

但是若比较不同容器,不同类型:

scala> List(1,2,3)==Set(1,2,3)res12: Boolean = false

不会相同
scala的’==’运算符相当于Java中的.equals()方法
2.函数式对象
根据书上的例子,直接先贴上一段例子:

下面是一个分数类:

implicit def intToRational(x:Int)=new Rational(x)   //创造隐式转换class Rational(n:Int,d:Int){                            //Scala的主构造器    require(d!=0)                                   //构造的先决条件    private val g=gcd(n,d)                          //分子分母的最大公约数    val num=n/g                                     //分子    val demon=d/g                                   //分母    override def toString=if (demon!=1) num+"/"+demon else num+""   //重载toString方法    def this(n:Int)=this(n,1)                       //从构造器    def +(that:Rational):Rational={                 //重载(overload)运算符'+'        val nums=num*that.demon+that.num        val demons=demon*that.demon        val randent=gcd(num*that.demon+that.num,demon*that.demon)        new Rational(nums/randent,demons/randent)        }    def +(that:Int):Rational={        val nums=that*demon+num        val randent=gcd(nums,demon)        new Rational(nums/randent,demon/randent)        }    def *(that:Rational):Rational=        new Rational(num*that.num,demon*that.demon)    def *(that:Int):Rational=        new Rational(num*that,demon)    private def gcd(a:Int,b:Int):Int=        if(b==0) a        else            gcd(a min b,(a max b)%(a min b))}
  • 主构造器

传入的两个参数均是不可变的val类型,关于不可变对象的优势书上的解释更加具体:

首先,不可变对象常
常比可变对象更具逻辑性,因为它们没有随着时间而变化的复杂的状态空间。
其次,你可以很自由地传递不可变对象,而或许需要在把可变对象传递给其它代码之前,需要先建造
个以防万一的副本。
第三,没有机会能让两个同时访问不可变对象的线程破坏它合理构造的状态,因为根本没有线程可以改变不可变对象的状态。
第四,不可变对象让哈希表键值更安全。
——《Scala编程》p85

  • 先决条件
    如果没有符合先决条件,则会抛出IllegalArgumentException异常
  • add运算
    注意(that:Rational)参数列表中的that并不指向被调用的参数,只能访问类中的字段。所以在类中重新定义了num和demon作为分子和分母,以供调用。关于对象指向,书上有一段解释:

    实际上,在 that 指的是调用 add 的对象时, Rational 可以加到自己身上。但是因为你可以传递任何
    Rational 对象给 add ,所以编译器仍然不会让你说 that.n 。

  • 从构造器
    发现在Java中其实也有类似的定义方法,如定义多个构造函数,但是手动设置默认的参数,当作有缺省值的构造函数调用。

    Scala 的类里面,只有主构造器可以调用超类的构造器。
    —–《Scala编程》p89

3.控制结构

  • While语句
    因为Scala是偏向于函数式编程的语言,所以应该尽量减少使用while循环,在函数式编程中,循环大多使用尾递归得到执行,可以使代码更加简略。
  • for语句
    for是一种枚举表达式:
val filesHere = (new java.io.File("\home")).listFilesfor (file <- filesHere)println(file)

上文可将\home目录下的文件遍历输出
同时for语句还可以用if过滤:

    val filesHere = (new java.io.File("\home")).listFiles    for (file <- filesHere if file.getName.endsWith(".scala"))    println(file)

输出文件后缀为.scala的文件,因为if子句是返回值的,所以语句没有语法问题。
for中可以不止只使用一种条件判断,用大括号能进行嵌套:

def fileLines(file:java.io.File)=scala.io.Source.fromFile(file).getLines.toList def grep(pattern: String) = for { file <- filesHere  if file.getName.endsWith(".scala") line <- fileLines(file)  if line.trim.matches(pattern)  } println(file + ": " + line.trim)  grep(".*gcd.*")    

用了书上的例子,在枚举的过程中使用多个条件分支进行过滤。

  • yield
    yield关键字可以生成枚举过滤之后产生的结果集,基本语句:
for {子句} yield {循环体}

例如该例:

val forLineLengths = for { file <- filesHere line <- fileLines(file) trimmed = line.trim if trimmed.matches(".*for.*") } yield trimmed.length

产生一个文件中包含“for”字符串的长度数组,所以for的返回类型就是Array[Int]
4.函数和闭包

  • 本地函数
    Scala可以在函数里面定义函数,例如:
def processFile(filename: String, width: Int) {    def processLine(line:String) {    if (line.length > width) print(filename+": "+line)    }val source = Source.fromFile(filename)for (line <- source.getLines) {processLine(filename, width, line)}}

注意到帮助函数processLine(line:String)只有一个参数,因为在本地函数内,外部函数的参数是共享的。

  • 第一类函数
    Scala中函数可以写成文本的形式直接返回值:
(x: Int) => x + 1

例如上面就是一个函数文本箭头右边的是函数体,左边则是参数。
作为函数文本的应用,可以写成如下形式赋值给变量:

var increase=(x:Int)=>x+1

调用increase(2),则可返回值3。此处函数作为一个对象返回值。
同时,如果函数体并不能用一句话写出的话,需要用到括号。

  • 函数占位符
    例如定义下面一个集合:
val a=Set(1,2,-9,4,-5)

如果要过滤掉其中的负数,可以用以下语句:

 a.filter(_ >0)

_ 就代表占位符号,看作表达式中应该被填入的参数。

a.foreach(println _)

这句话也是占位符使用的常例
占位符可以使函数更加偏于应用方面,比如定义一个sum函数:

def sum(a:Int,b:Int,c:Int):Int a+b+c

那么我们可以如下定义一个a函数:

val a=sum _a(1,2,3)

也可以如下定义b函数:

val b=sum(1, _:Int,3)b(2)

可以使得函数的定义趋近于灵活。占位符可看作一种缺省值,这个缺省值就是新定义的函数的参数列表。

  • 函数闭包
    如果在前面的函数文本中改变一下:
(x:Int)=>x+more

我们发现多了一个未知的变量more,这个变量more在闭包里就称为自由变量,x是一个绑定变量。这个函数文本就可以称为一个闭包,而:

(x:Int)=>x+1

这样的值有绑定变量的函数文本是已经封闭的了,闭包的意思是捕获自由变量,即当自由变量已经定义时,闭包对自由变量的作用就是去绑定自由变量。
闭包的应用:

def makeIncreaser(more: Int) = (x: Int) => x + moreval inc=makeIncreaser(1)inc(2)

得到结果res:3
定义一个函数就是确定闭包的自由变量。

  • 重复参数
    Scala的重复参数类似与Java的可变参数概念,例如Java中的:
public static void main(String...args){...}

其中的参数args就是一个String类型的可变参数,其大小是由传入时才决定的,实质上可看作一个数组。Scala也是这样,只不过Scala使用’*’号表示:

def getArray(args:String*)={...}
  • 尾递归
    我们看一下这个例子,输出给定的n到10之间的所有数字(很。。简单吧。。。)
def Increase(more:Int)=(x:Int)=>x+moreval plusplus=Increase(1)val judge= (x:Int)=>if(x==10) true else falsedef delayTen(n:Int):Int={print(n)if(judge(n))    nelse delayTen(plusplus(n))}val p=(n:Int)=>print(delayTen(n))p(2)

其实用Java的while循环或者for循环就可以解决,但是Scala鼓励使用函数式编程,所以尾递归是一种代替循环的方法。Scala会在尾递归过程做优化。
尾递归的定义:

在它们最后一个动作调用自己的函数,被称为尾递归
—–《Scala编程》p122

尾递归的优化:

Scala 编译器检测到尾递归就用新值更新函数参数,然后把它替换成一个回到函数开头的跳转。
—–《Scala编程》 p122


感觉这两天的学习大多依靠着书本,虽然也跟着敲一些简单的示例和变化,但是总觉得还停留在很浅显的层次上。尤其是对函数式编程的理解,只是跟着书上的代码好像理解都还是表面上的,真正地如何运用函数式编程的精髓依然没有弄透。当然是为了Spark才开始着手学习Scala的,现在要结合这Spark上面的例子来实践了,然后再从书上学不会的东西。

0 0
原创粉丝点击