scala 学习笔记(scala for the impatient)

蒋茂材
2023-12-01

scala 融合了面相对编程和面向对象编程的思想,现在java8出世了,其中的lambda表达式和scala的语法很相似,估计面向函数编程会成为一种趋势。

    通过这段时间的学习,最大的感觉就是scala语法相对于java来说要简洁得多,面向函数编程确实令人眼前一亮,下面记录下今天所学习知识的笔记。

    (1)、表达式:scala的条件表达式即if/else有值,可以将此值赋给变量,这是在java不能实现的,如val s = if(x > 0) 1 else –1。如果else部分缺失了的话,如val s2 = if(x > 0) 1,如果x 不大于0的话,此时s2的值为unit,写作()。此时表达式相当于val s2  = if(x > 0) 1 else ()。

     (2)、语句终止:在java中每一个语句的是以分号结束的。而scala与javaScript和其他脚本语言类似,行尾位置不需要分号。如果在单行写下多个语句,就需要将它们以分号分开。如 if(n > 0){ r = r  *  n; n –= 1}。

     (3)、块表达式和赋值:在java中,块语句包含于{}中的语句序列,每当需要在逻辑分支或循环中放置多个动作时,都可以使用块语句。在scala中,{}包含一系列表达式,块最后一个表达式的值就是块的值。此特性对于那种对某个val需要分多步初始化时非常有用。如val distance = {val dx = x – x0; val dy = y – y0; sqrt(dx * dx + dy * dy)}。此时distance的值为sqrt(dx * dx + dy * dy)。赋值动作本身是没有值的,或者说值为unit类型。unit相当于java中的void,因此在scala中不能如下这样写:x =y =1

     (4)、输入和输出:scala中从控制台输入信息,用readLine、readInt、readDouble等

     (5)、循环:scala和java拥有想通的while和do循环。如 while (n > 0) {r = r * n; n –= 1},scala没有与java中循环对应的结构,scala中for语句:

for( i <- 1 to 10)&#160; s= s+1 ,此表达式中i无需声明类型,该变量的类型是集合元素的类型。当遍历字符串或者数组时,通常需要使用0到n-1的区间,此时可以用until方法,for(i <- 0 until “adsad”.length) sum += s(i);此处是为了展示until的用法,此式例可以用这种写法替代: for( ch <- “adsad”) sum += ch 。scala的for循环有很丰富的功能,可以用 “变量<-表达式”提供多个生成器,用分号将它们隔开,如:for(i <- 1 to 4; j <-1 to 7) print(i * j + “ ”),并且每个生成器可以带一个守卫(以if开头的Boolean表达式),如:for(i <- 1 to 3;j <- 1 to 7 if i != j ) print(i * j "+ “ ”),第二个生成器带了 “if i != j”守卫。除了守卫之外我们还可以使用任意多的定义,如:for(i <- 1 to 3;x = i + 1;j <- x to 7 if i != j ) print(i * j "+ “ ”)。scala的for循环还可以以yield开始构造一个新的集合,每次迭代生成新集合中的一个值,如:for(i <- 1 to 10) yield i + 1 ,此时生成vector(2,3,4,5, … ,11),这类循环叫做for推导式,for推导式生成的集合和它的第一个生成器是兼容的。

    (6)、函数:scala除了方法还支持函数,方法对对象进行操作,函数不是,在java中我们只能用静态方法来模拟。函数定义:def f1(x: Double) = if (x > 0) x else –x ,只要函数不是递归的,就不需要指定返回类型,scala编译器可以通过”=”号右侧的表达式类型推断出返回类型。对于递归函数,我们就必须指定返回类型,如:def f(n: Int): Int = if(n < 0) 1 else f(n - 1),如果没有返回类型,scala编译器无法校验 f(n - 1)的类型是Int。

    (7)、默认参数和带名参数: 默认参数,def f(str: String, left: String = “xx”,right: String = “yy”) = left + str + right;带名参数:调用函数的时候,如果在提供参数值的时候指定了参数名,则参数顺序并不需要跟参数列表的顺序一致,如:def(left=”ss”,right=”xx”,str = “abccd”)

    (8)、变长参数:顾名思义就是你可以使用任意多的参数来调用此函数,如:def sum(args: Int*) = {var r = 0; for(arg <- args) r += arg; r},调用此函数:val s =sum(2,34,23,12,23) 。函数得到一个类型为Seq的参数,如果想将一个有值的序列当做参数序列处理,你得告诉编译器你希望这个参数被当做参数序列处理,追加“:_*”,如:val s = sum(1 to 34:_*) 

    (9)、过程:如果函数体包含在{}中但没有前面的=号,那么返回类型就是Unit,这种函数就称作过程。我们调用过程是为了它的副作用,比如把消息输出到控制台等等。

     (10)、懒值:当val被声明为懒值(lazy)时,它的初始化将被延迟,直到我们首次对它取值。如:lazy val str = scala.io.Source.fromFile("c/user/abc").mkString。如果程序不访问str,则文件不会被打开。懒值对于开销较大的初始化语言很有用。懒值处于val和def的中间状态。

    (11)、异常:scala的异常工作机制和java基本上差不多,与java所不同的是scala没有“受检”异常,throw表达式有特殊类型Nothing。在if/else中很有用,如果一个分支的类型为Nothing,那么if/else表达式类型就是另一个分支的类型。scala捕获异常采用模式匹配的语法,如:

1 try
2     process(new URL("https://www.google.com") )
3 }
4 catch {
5  case _: MalformedURLException => println("Bad URL:" + url )
6  case ex: IOException => ex.PrintStackTrace()
7 }

如果你不需要使用捕获的一场对象,可以使用_来替代变量名。

 类似资料: