Scala Basics

//20170809 周三
#def patch [B >: Char, That] (from: Int, patch: GenSeq[B], replaced: Int)
(implicit bf: CanBuildFrom(String, B, That]): That

等价于
==>def patch(from: Int, that: GenSeq[Char], replaced: Int): StringOps[A]

可以把GenSeq[Char]和StringOps[A]都当做String。在REPL中试用: "Harry".patch(1, "ung", 2) //将产出"Hungry"


#在for循环的变量之前并没有val或var的指定,该变量的类型是集合的元素类型,变量的作用域一直持续到循环结束。


#遍历string或array时,常需要试用从0到n-1的区间,可以用until方法而不是to方法。
val s = "Hello"
var sum = 0
for (i <- 0 util s.length) //i的最后一个取值是s.length - 1
    sum += s(i)

等价于(也可以不使用下标,直接遍历对应的字符序列)
==>
var sum = 0
for (ch <- "Hello") sum += ch


在Scala中,对loop的使用不频繁;通常我们可以通过对序列中的所有值应用某个函数的方式来处理它们,而完成这项工作只需要一次方法调用即可。


#Scala没有提供break或continue语句来退出循环。如果需要break时,有如下几个选项:
1.使用Boolean型的控制变量。
2.使用嵌套函数——可以从函数当中return3.使用Break对象中的break方法:
import scala.util.control.Breaks.
breakbable {
    for (...) {
        if (...) break; //退出breakable块
        ...
    }
}

在这里,控制权的转移是通过抛出和捕获异常完成的,因此,如果时间很重要的话,应该尽量避免使用这套机制。


#高级for循环
-1.可以以 variable<-expression 的形式提供多个生成器(用分号隔开),每个生成器都可以带一个守卫(以if开头的Boolean表达式,注意在if之前**没有分号**)
for (i <- 1 to 3; j <- 1 to 3 if i !=j) print ((10 * i + j) + " ")//将打印12 13 21 23 31 32

-2.可以使用任意多的定义,引入可以在loop中使用的variable:
for (i <- 1 to 3; from = 4 -j; j <- from to 3) print ((10 * i + j) + " ")//将打印13 22 23 31 32 33

-3.for推导式
for推导式生成的集合与它的第一个生成器是类型兼容的。
for (i <- 1 to 10) yield i % 3 //生成Vector(1, 2, 0, 1, 2, 0, 1, 2, 0, 1)
for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar //生成"HIeflmlmop"
for (i <- 0 to 1; c <- "Hello") yield (c + i).toChar //生成Vector('H', 'e', 'l', 'l', 'o', 'I', 'f', 'm', 'm', 'p')

-4.也可以将生成器、守卫和定义包含在花括号当中,并可以以换行的方式而不是分号来隔开它们:
for { i <- 1 to 3
    from = 4 - i
    j <- from to 3
}


#Scala函数
1.方法对对象进行操作,函数不是。在Java中只能用静态方法来模拟函数。
2.只要函数不是递归的,就不需要指定返回类型。对于递归函数,必须指定返回类型。
3.Scala编译器可以通过 = 右侧的表达式的类型推断出返回类型。
4.如果函数体需要多个表达式完成,可以用代码块,块中最后一个表达式的值就是函数的返回值。
5.Scala函数中尽量少用return6.**匿名函数**——这些函数中return并不返回值给调用者,它跳出到包含它的带名函数中;我们可以把return当做是函数版的break语句,仅在需要时使用。


def abs(x: Double) = if(x >= 0) x else -x //绝对值函数

def fac(n: Int) = { //阶乘函数,函数返回位于for循环之后的r的值
    var r = 1 
    for(i <- 1 to n)  r = r * i
    r
}

def fac(n: Int): Int = if(n <= 0) 1 else n * fac(n - 1)
//如果没有返回类型,Scala编译器无法校验n*fac(n-1)的类型是Int。

7.some language can infer the kind of recursion function by Hindley-Milner algorithm,like ML and Haskell.不过,在面向对象语言中这样做并不总是行得通,如何扩展Hindley-Milner 

algorithm让它能够处理子类型仍然是个科研命题。


#默认参数和带名参数
//默认参数
def decorate(str: String, left: String = "[", right: String = "]") = left + str +right  
/*decorate——装饰函数,left和right是默认参数,带有默认值"[""]"。
 *decorate("hello"),得到[hello]。
 *decorate("hello", "<<<", ">>>"),得到<<<hello>>>。
 *如果给出的值的数量少于参数数量,默认参数会从后往前逐个应用进来:
 *decorate("hello", ">>>["),会使用right参数的默认值,得到>>>[hello]。
 */

//带名参数——在提供参数值的时候指定参数名.注意带名参数并不需要跟参数列表的顺序完全一致。
decorate(left = "<<<", str = "hello", right = ">>>"),得到<<<hello>>>。

//可以混用未命名参数和带名参数,只要那些未命名参数是排在前面的即可:
decorate("Hello", right = "]<<<"),将调用decorate("Hello", "[", "]<<<")得到[hello]<<<。


#变长参数
def sum(args: Int*) = {
    var result = 0
    for(arg <- args)  result += arg
    result
}

1.函数得到的是一个类型为Seq的参数。
eg:val s = sum(1, 4, 9 , 16, 25)

2.如果已经有一个值的序列,则不能直接将它传入上述函数。
eg:val s = sum(1 to 5)  //错误

3.如果sum函数被调用时传入的是单个参数,那么该参数必须是单个整数,而不是一个整数区间。解决这个问题的办法是告诉编译器你希望这个参数被当做参数序列处理,追加: _*。
eg:val s = sum(1 to 5: _*)  //将1 to 5当做参数序列处理

4.在递归定义中会用到变长参数。
eg:
def recursiveSum(args: Int*): Int = {
    if(args.length == 0) 0
    else args.head + recursiveSum(args.tail: _*)
}
//在这里,序列的head是它的首个元素,而tail是所有其他元素的序列,这又是一个Seq,用: _*来将它转换成参数序列。

5.当调用变长参数且参数类型为Object的Java方法时,如PrintStream.printf或MessageFormat.format时,需要手工对基本类型进行转换。
eg:val str = MessageFormat.format("The answer to {0} is {1}", "everything", 42.asInstanceOf[AnyRef])
对于任何Object类型的参数都是这样,这里之所以指出,是因为类似的参数在变长参数方法中用的最多。


#过程(procedure)——不返回值的函数,左花括号前没有=,那么返回类型就是Unit。


#懒值——当val被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。
eg:lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString
如果程序从不访问words,那么文件也不会被打开。为了验证这个行为,可以在REPL中试验,故意拼错文件名。在初始化语句被执行的时候并不会报错。不过,一旦你访问words,就将会得到一个错误提示:文件未找到。

1.懒值对于开销较大的初始化语句非常有用,比如循环依赖;懒值更是开发懒数据结构的基础。

2.可以把懒值当做是介于val和def的中间状态,对比如下定义:
val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString
//在words被定义时即被取值
lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString
//words被首次使用时取值
def words = scala.io.Source.fromFile("/usr/share/dict/words").mkString
//在每一次words被使用时取值


#异常
throw new IllegalArgumentException("x should not be negative")

1.和Java一样,抛出的对象必须是java.lang.Throwable的子类;不过,与Java不同的是Scala没有"受检"异常——不需要声明说函数或方法可能会抛出某种异常。

2.在Java中,"受检"异常在编译器被检查。如果你的方法可能会抛出IOException,你必须做出声明。这就要求程序员必须去向哪些异常应该在哪里被处理掉,这是个值得称道的目标。不幸的是,它同时

催生出奇怪的方法签名,比如void doSomething() throws IOException,InterruptedException,ClassNotFoundException。许多Java程序员反感这个特性,最终过早捕获这些异常,或者使用超通用的异

常类。Scala设计者们决定不支持"受检"异常,因为他们意识到彻底的编译期检查并不总是好的。

3.throw表达式有特殊的类型Nothing,如果一个分支的类型是Nothing,那么if/else表达式的类型就是另一个分支的类型。
eg:
if(x >= 0) { sqrt(x) } else throw new IllegalArgumentException("x should not be negative")
第一个分支类型是Double,第二个分支类型是Nothing;因此if/else表达式的类型就是Double。

4.捕获异常的语法采用的是模式匹配。
try {
    process(new URL("http://horstmann.com/fred-tiny.gif))
} catch {
    case _:MalformedURLException => println("Bad URL: " + url)
    case ex: IOException => ex.printStackTrace()
}

5.和Java或C++一样,更通用的异常应该排在更具体的异常之后。注意,如果不需要使用捕获的异常对象,可以使用_来替代变量名。try/finally语句让你可以释放资源,不论有没有异常发生。
eg:
var in = new URL("http://horstmann.com/fred.gif").openStream()
try {
    process(in)
} finally {
    in.close()
}

6.finally 语句不论process函数释放抛出异常都会执行,reader总会关闭。这段代码有些微妙:
- 如果URL构造器或openStream方法抛出异常怎么办?这样一来try代码块和finally语句都不会被执行。这没啥不好——in从未被初始化,因此调用close方法没有意义。
- 为啥val in = new URL(...).openStream()不放在try代码块里?因为这样做的话in的作用域不会延展到finally语句当中。
- 如果in.close()抛出异常怎么办?这样一来异常跳出当前语句,废弃并替代掉所有先前抛出的异常。(这跟Java一模一样,并不是很完美。理想情况是老的异常应该与新的异常一起保留。)

7.注意:try/catch和try/finally的目的是互补的。try/catch语句处理异常,而try/finally语句在异常没有被处理时执行某种动作(通常是清理工作)。可以把它们结合在一起成为单个

try/catch/finally语句:
try {...} catch {...} finally {...}
这和下面的语句一样:
try { try {...} catch {...} } finally {...}
不过,这样组合在一起的写法几乎没什么用。


#数组
http://blog.csdn.net/u010994304/article/details/51752415


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值