快学Scala 自学笔记
第一至第四章
参考
http://blog.csdn.net/escaflone/article/details/43485345
1、变量声明
常用类型:
scala的 StringOps, RichInt, RichDouble, RichChart 给基本类型提供了很多方法,在调用时会自动隐式转换
类型转换 用方法
apply方法 类似函数调用的语法
2、控制结构和函数
for 循环
高级for循环和for 推导式
函数
默认参数和带名参数
变长参数
过程
懒值
异常
3、数组
遍历数组
数组转换
常用算法
多维数组
与Java的互操作
4、映射和元素
更新Map
迭代Map
排序Map
与Java的互操作
元组
拉链操作
-------------------------------
http://www.importnew.com/4307.html
Scala教程:高级类型
第一至第四章
参考
http://blog.csdn.net/escaflone/article/details/43485345
1、变量声明
val answer = 8 * 5 + 2; //常量
var counter = 0; //变量
//在必要的时候 ,可以指定类型
val greeting:String = null
val greeting:Any = "Hello"
//可以将多个值或变量放在一起声明
val xmax, ymax = 100 //xmax 和 ymax设为100
var greeting, message:String = null // 都被设为字符串,被初始化为null
常用类型:
常用数值类型
Byte Char Short Int Long Float Double Boolean
BigInteger BigDecimal 用于任意大小的数字
scala的 StringOps, RichInt, RichDouble, RichChart 给基本类型提供了很多方法,在调用时会自动隐式转换
"Hello".intersect("World") //输出"lo" 输出两个字符串共通的一组字符
1.to(10) //Int值1首先被转换为RichInt,然后调用to方法
类型转换 用方法
不像java用 强制类型转换,而是用方法
99.44.toInt //99, 这里不带参数,并且不改变当前对象的方法 通常不实用圆括号
99.toChart //'c'
99.toString //"99"
"99.44".toDouble // 99.44
apply方法 类似函数调用的语法
"Hello"(4) // '0'
实际上是StringOps类的apply方法
def apply(n:Int) : Char
"Hello".apply(4) // 完整写法
BigInt伴生对象的apply方法:
BigInt("1234567890") // BigInt.apply("1234567890")
BigInt("123") * BigInt("123")
数组Array,也是伴生对象
Array(1,4,9,16)
2、控制结构和函数
if (x > 0) 1 else -1
可以将if/else表达式的值赋给变量
val s = if(x > 0) 1 else -1
每个表达式都有一个类型
if(x > 0) 1 else -1 // Int
if(x > 0) "p" else -1 // Any (Int 和String的超类)
if(x > 0) 1 // (),Unit类,因为可能没有输出值,即if(x >0) 1 else ()
while(n > 0){
r = r*n
n -=1
}
for 循环
for( i <- 表达式)
for( i <- 1 to 10)
r = r*i
val s = "Hello"
var sum = 0
for( i <- 0 until s.length)
sum+= s(i)
简化版: //这个没看懂
var sum = 0
for(ch <- "Hello") sum+=ch
高级for循环和for 推导式
for( i <- 1 to 3; j <- 1 to 3) println((10 * i + j)+ "")
11 12 13 21 22 23 31 32 33
for(i <- 1 to 3; j <- 1 to 3 if i != j)print((10 * i + j)+ "")
12 13 21 23 31 32
注意在if前没有分号
from是在循环中使用的变量
for(i <- 3; from = 4 - i ; j <- from to 3) print((10 * i + j) + " ")
31 22 23 31 32 33
以yield开始,则循环会构造出一个集合,每次迭代生成集合中的一个值
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
"Hilflmlmop"
for(i <- 0 to 1; c <- "Hello") yield (c + i).toChar
Vector('H','e','l',''l','o','I','f','m','m','p')
函数
def abs(x:Double) = if(x >=0) x else -x
def fac(n:Int) = {
var r=1
for( i<- 1 to n) r=r*i
r // 无需return,最后一行即返回值
}
一般定义函数无需指定返回类型,Scala可以通过=符号右侧的表示式的类型推断返回类型,除非是递归函数。
对于递归函数,必须指定返回类型
def fac(n:Int) :Int = if(n<=0) 1 else n* fac(n-1)// 如果没有返回类型,Scala无法校验 n*fac(n-1)的类型是Int
默认参数和带名参数
//left默认值"["
def decorate(str:String, left:String="[", right:String ="]") = left + str +right
decorate("Hello", "<<<", ">>>") // <<<Hello>>>
decorate("Hello","<<<") // <<<Hello]
decorate(left = "<<<", str = "Hello" , right = ">>>") // 指定参数名,<<<Hello>>>
decorate("Hello", right="]<<<") // 混用<<<Hello]<<<
变长参数
实现一个可以接受可变长度参数列表的函数会更方便。
def sum(args: Int*){
var result = 0
for (arg <- args) result += arg
result
}
val s = sum(1,4,9,16,25) //函数得到 类型Seq的参数 Seq类型可以用for循环来访问每一个元素
val s = sum(1 to 5) // 错误
val s = sum(1 to 5: _*) //将 1 to 5当做参数序列处理
递归中使用
def recursiveSum(args : Int *): Int = {
if (args.length ==0) 0
else args.head + recursiveSum(args.tail:_*) // head是首个元素, tail是所有其他元素的序列,是一个Seq
}
过程
过程不返回任何值,可以略去=号
def box(x:String) {
println(x)
}
也可以显式声明Unit返回类型
def box(x:String) :Unit = {}</span>
懒值
lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString //直到我们首次对它取值才会初始化。
可以把懒值当做介于 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//在首次调用
def words= scala.io.Source.fromFile("/usr/share/dict/words").mkString//在每一次被使用时
异常
if (x >= 0 ) { sqrt(x) }
else throw new IllegalArgumentException("x should not be negative")
throw 表达式有特殊的类型Nothing, 在if/else中,如果以个分支的类型为Nothing, 那么表达式的类型就是 另一个分支的类型, 在这里就是 Double
捕获异常,使用模式匹配
try{
}catch{
case _: MalformedURLException => println("Bad Url:" + url)
case ex: IOException => ex.printStackTrace()
}
var in = new URL("http://xxx.com").openStream()
try{
process(in)
}finally{
in.close()
}
try{...} catch{...} finally{...}
3、数组
定长数组
val nums = new Array[Int](10) // 都是0
val a = new Array[String](10) // 都是null
val s = Array("Hello", "World") // Array伴生对象的apply 方法
s(0) = "Goodbye" //Array("Goodbye","World")
变长数组
import scala.collection.mutable.ArrayBuffer
val b = ArrayBuffer[Int]() // 伴生对象的apply
//或者 new ArrayBuffer[Int]
b += 1 //(1)
b += (1,2,3,5) // (1,1,2,3,5)
b ++= Array(8,13,21) // 可以用++=追加任何集合 (1,1,2,3,5,8,13,21)
b.trimEnd(5) //移除最后5个元素 , (1,1,2)
b.insert(2,6) // (1,1,6,2)
b.insert(2,7,8,9) // (1,1,7,8,9,6,2)
b.remove(2) // (1,1,8,9,6,2)
b.remove(2,3) //(1,1,2)
b.toArray // Array(1,1,2) ,变长转定长
a.toBuffer // 定长转变长
遍历数组
for(i <- 0 until.a.length)
println(i + ": " + a(i))
如果在循环体中不需要数组下标,可以直接访问数组元素
for(elem <- a )
println(elem)
elem先后被设为a(0),a(1),依此类推。
数组转换
for (...) yield 循环创建了一个类型与原始集合相同的新集合。
val a = Array(2,3,5,7,11)
val result = for(elem <- a) yield 2 * elem //yield 有疑问?
// result 是 Array(4, 6, 10 , 14, 22)
对每个偶数翻倍,并丢掉奇数元素
for(elem <- a if elem %2 ==0) yield 2 * elem
另一种做法
a.filter(_ % 2 ==2).map(2 * _)
移除第一个负数之外的所有负数
首先收集需要保留的下标:
var first = true
val indexs = for(i <-0 until a.length if first || a(i) >=0) yield {
if(a(i)<0) first = false; i
// 按书序记录了所有正数和第一个负数的小标,其他负数的小标都丢弃了
for(j <- 0 until.indexs.length) a(j) = a(index(j))//将元素移动到该去的位置 a.trimEnd(a.length - indexs.length)//并截断尾端//这里主要是 小标处理
常用算法
数组:Array(1,7,2,9).sum //19
数组缓冲:ArrayBuffer("Mary","had","a","little",'lamb').max //"little"
val b = ArrayBuffer(1,7,2,9)
val bSorted = b.sorted //b没有改变
bSorted是ArrayBuffer(1,2,7,9)
提供一个比较函数,用sortWith方法
val bSorted = b.sorted(_ < _)// (1,2,7,9) 这里b没有改变
直接对一个数组排序,单不能对数组缓冲排序
val a = Array(1,7,2,9)
scala.util.Sorting.quickSort(a) // 此方法不适于ArrayBuffer
想要显示数组或数组缓冲的内容,可以用mkString
a.mkString(" and ") // "1 and 2 and 7 and 9"
a.mkString("<", "," , ">")// "<1,2,7,9>"
多维数组
通过数组的数组来实现:
Array[Array[Double]]
也可以通过ofDim方法:
val matrix = Array.ofDim[Double](3,4) // 三行,四列
要访问其中的元素,使用两对圆括号:
matrix(row)(col) = 42
创建不规则的多维数组
var triangle = new Array[Array[Int]](10)
for(i <- 0 until triangle.length)
triangle(i) = new Array[Int](i+1)
与Java的互操作
通过引入scala.collection.JavaConversions里的隐式转换方法,可以自动包装成Java对象,如列表等。
import scala.collection.JavaConversions.bufferAsJavaList
import scala.collection.mutable.ArrayBuffer
val command = ArrayBuffer("ls", "-all" , "/home/clat")
val pb = new ProcessBuilder(command)
// Scala to Java, Scala缓冲数组被包装成一个实现了java.until.List接口的Java类的对象
反过来,把Java.until.List,自动转换成一个Buffer
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable.Buffer
val cmd :Buffer[String] = pb.command()
// java to scala, 不能使用ArrayBuffer, 包装起来的对象仅能保证是个Buffer
4、映射和元素
val scores = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8 ) //不可变Map[String, Int]
val scores = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8 ) //可变Map[String, Int]
val scores = new scala.collection.mutable.HashMap[String, Int] //定义一个空的Map,需要给出类型参数
Map 是对偶的集合(两个值构成的组), -> 操作符 用来创建对偶, "Alice" -> 10 产出的值是 ("Alice", 10)
所以也可以用下面的方法定义 Map:
val scores = Map(("Alice",10),("Bob",3),("Cindy",8)) //只不过 -> 操作符 比 圆括号 更易读,更符合大家对Map的直观感觉。
获取Map中的值
使用()表示法来查找某个键对应的值
val bobScore = scores("Bob") // 类似java的 scores.get("Bob"), 如果不存在,则抛出异常
检查是否有某个制定的键,可以用contains方法
val bobsScore = if(scores.contains("Bob")) scores("Bob") else 0 // 可以用contains 来判断
val bobsScore = scores.getOrElse("Bob",0) //返回Bob键对应的值;否则,返回0
映射.get(键)这样的调用返回一个Option对象,要么是Some(键对应值),要么是None.
更新Map
可变的Map
score("Bob") = 10 //更新
scores("Fred") = 7 //增加
scores += ("Bob" -> 10, "Fred"-> 7)
scoers -= "Alice"
不可变Map, 假定当前scores是不可变
val new Scores = scores + ("Bob" -> 10, "Fred"-> 7) // 产生一个新的Map
或者 当 scores 是 var变量
var scores = ...
scores = scores + ("Bob" -> 10, "Fred"-> 7)
scores = scores - "Alice"
迭代Map
for( (k, v) <- Map) 处理 k 和 v
scores.keySet //一个类似 Set("Bob", "Cindy", "Alice")这样的集合
for(v <- scores.values) prinltn(v) // 打印 10 8 7 10
for( (k, v) <- map) yield ( v, k ) //反转Map
排序Map
默认Scala给的是 哈希表,
val scores = scala.collections.immutable.SortedMap("Alice" -> 10, "Fred" -> 7, "Bob" -> 3, "Cindy" -> 8) // 如果需要排序,需要用树形Map
在Scala(2.9)中,还没有可变的 树形Map,只能用java的TreeMap
如果想按插入顺序访问所有键,可以用LinkedHashMap
val months = scala.collection.mutable.LinkedHashMap("Jan" ->1, ,"Feb" ->2, "Mar" ->3 , ...)
与Java的互操作
Java -> Scala
引入语句
import scala.collection.JavaConversions.mapAsScalaMap
val scores: scala.collection.mutable.Map[String, Int] = new java.util.TreeMap[String,Int]
import scala.collection.JavaConversions.propertiesAsScalaMap
val props:scala.collection.Map[String,String] = System.getProperties()
Scala -> Java
import scala.collection.JavaConversions.mapAsJavaMap
import java.awt.font.TextAttribute._
val attrs = Map(FAMILY -> "Serif" , SIZE -> 12)
val font = new javal.awt.Font(attrs) //该方法预期一个Java Map ?有疑问
元组
Map是k/v对偶的集合,对偶是元组最简单的形态, 元组是不同类型的值的聚集。
val t = (1,3.14,"Fred") // Tuple3[Int, Double, java.lang.String]
val second = t._2
// 元组位置从1开始,这里是 3.14
//t._2 可以写成 t _2, 中间变空格
使用模式匹配来获取元组的组元 此处有疑问?
val (first, second, thrid) = t // first设为1, second 设为3.14, third设为 "Fred"
当不需要所有部件时,可以在不需要的位置上使用_:
val (first, second, _) = t
元组用于函数需要范围不止一个值得情况,如StringOps的partition方法,返回包含满足和不满足某条件的字符:
"New York".partition(_.isUpper) // ("NY" , "ew ork")
拉链操作
使用元组的原因之一是把多个值绑在一起,以便它们能够被一起处理,这通常可以用zip方法来完成。
val symbols = Array("<", "-", ">")
val counts = Array(2, 10 , 2)
val pairs = symbols.zip(counts) // 输出对偶的数组, Array(("<", 2), ("-", 10), (">", 2))
然后这些对偶就可以被一起处理:
for((s,n) <- pairs) Console.print(s * n)// 会打印 <<---------->>
用toMap方法可以将对偶的集合转换成Map
keys.zip(values).toMap//keys是k集合, values是与之平行对应的v集合
-------------------------------
http://www.importnew.com/4307.html
Scala教程:高级类型