Scala Related 25 Mar 2014
基本语法
val 类似于java中final变量,一经定义初始化后,就不能改变
val msg = "Hello, world!"var 定义一般变量,允许其值发生改变
var greeting = "Hello, world!"定义函数
def max(x: Int, y: Int): Int = {
if (x > y) x
else y
}当函数体只有一行时,可以简写成如下形式
def max2(x: Int, y: Int) = if (x > y) x else y所有函数都通过def关键词来定义,接下来max是函数名,()中是参数列表,x和y是参数名,其对应的类型,通过:分割,参数列表后紧跟:返回值类型,然后是=函数体
// 定义一个无参无返回值的函数
def greet() = println("Hello, world!")
// greet: ()Unitgreet:后括号代表无参数,Unit代表没有返回值,类似于java中的void
scala脚本
// hello.scala
println("Hello, world, from a script!")使用 $ scala hello.scala 运行,结果显示
// helloarg.scala
// Say hello to the first argument
println("Hello, "+ args(0) +"!")使用 $ scala helloarg.scala planet 运行 planet是传递给脚本的参数,结果显示
循环和条件(Loop and Condition)
var i = 0
while (i < args.length) {
println(args(i))
i += 1
}
var i = 0
while (i < args.length) {
if (i != 0)
print(" ")
print(args(i))
i += 1
}
println()然而这并不是scala的风格,而是类似于java,C等语言的风格
下面是使用foreach完成循环功能
args.foreach(arg => println(arg))在上面的代码块中,args是一个列表/数组对象,foreach方法中,可以认为是你传递了一个匿名的函数(function literal),它以arg为参数,println(arg)为函数体
function literal
A function with no name in Scala source code, specified with function literal syntax. For example, (x: Int, y: Int) => x + y.
Scala解析器默认将arg当作String类型,如果你需要明确指定,可以采用下面语句
args.foreach((arg: String) => println(arg))如果你希望语句更简洁,当匿名函数的方法体只有一条语句,而且该语句只接收一个参数时,函数可以不指定函数名和参数,上述代码可以改写成下面形式
args.foreach(println)再看另外一个例子
for (arg <- args)
println(arg)<-符号右边的args是列表/数组,而<-符号左边的arg是一个val,而不是var,由于它一直是一个val,所以只需要写成arg即可。
数组(Array)
在Scala中,你可以用new关键词来实例化对象,类实例等,在实例化时,我们可以使用值和类型来初始化它。例如下面例子
val big = new java.math.BigInteger("12345")上面语句实例化了一个java实例java.math.BigInteger,并且使用“12345”来初始化它。
// Example 6.1
val greetStrings = new Array[String](3)
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!\n"
for (i <- 0 to 2)
print(greetStrings(i))上面例子中,greetStrings是一个Array[String]类型(an “array of string”)的实体,并且使用3来初始化该实体,表明该数组的长度为3,
如果你希望明确指定greetStrings的类型,可以使用如下语句
val greetStrings: Array[String] = new Array[String](3)在Example 6.1接下来三行,为greetStrings填充数据,和Java不同的是,greetStrings(0) = "Hello",Scala使用了圆括号(),而不是方括号[],由于greetStrings是一个val变量,所以它是不能变化的,你不能将它重新赋予另外一个不同的数组,但你可以改变它元素的值,数组本身是可变的。
接下来最后两行,第一行中显示了Scala的一个重要的规则,如果一个方法只接收一个参数,你可以不用点和括号来调用,在这个例子中,0 to 2可以转换成(0).to(2),to方法是Int实体0的一个方法,它接收一个Int参数(2)
Scala从技术上讲是没有操作符重载的,因为它没有传统意义上的操作符。而类似于+,-,*和/等符号都可以作为方法名,当你使用1+2的语句时,你实际上是在调用Int对象1的+方法,而2是传递给该方法的参数。所以可以将1+2改写为方法调用的形式,(1).+(2)
另外一个重要的特性表明了数组的访问为什么不是用方括号,而是用圆括号。当你在一个对象或实例上使用圆括号时,并为它传递了1个或多个参数,类似于greetStrings(1),Scala会将该语句转化成greetStrings.apply(1),所以在Scala中,数组的元素访问实际上也是一个方法调用。类似的,
greetStrings(0) = "Hello"可以转化为
greetStrings.update(0, "Hello")上面Example 6.1可以转化为方法调用的形式:
val greetStrings = new Array[String](3)
greetStrings.update(0, "Hello")
greetStrings.update(1, ", ")
greetStrings.update(2, "world!\n")
for (i <- 0.to(2))
print(greetStrings.apply(i))另外Scala还提供了数组的初始化的简单形式,
val numNames = Array("zero", "one", "two")上面语句中,Array实际上是一个名为Array的object,而不是类实例,而上面语句实际上是调用的Array对象的apply方法,该方法是一个工厂方法,实例化了一个Array类型的实体,并返回该实体。该apply方法接收一系列的值用于填充数组。上面语句还可以改写为
val numNames2 = Array.apply("zero", "one", "two")列表(List)
val oneTwoThree = List(1, 2, 3)上面已经提到,Scala中的数组Array是可变的,而列表List是不可变的,这一点与Java中的List不同,Java中的List是可变的。
val oneTwo = List(1, 2)
val threeFour = List(3, 4)
val oneTwoThreeFour = oneTwo ::: threeFour
println(""+ oneTwo +" and "+ threeFour +" were not mutated.")
println("Thus, "+ oneTwoThreeFour +" is a new list.")运行这段代码,结果是在屏幕上打印
List(1, 2) and List(3, 4) were not mutated.
Thus, List(1, 2, 3, 4) is a new list.
由于List是不可变的,所以它的行为String比较类似。List拥有一个名为“:::”的方法,它将创建一个新的List并将两个List的内容依次填充到新List中
另外一个List常用的方法是“::”
val twoThree = List(2, 3)
val oneTwoThree = 1 :: twoThree
println(oneTwoThree)程序运行结果是
“::”方法是在List前添加一个元素,在上面例子里就是在twoThree添加一个元素1。“::”是一个右运算符,它的拥有者是方法右边的twoThree,而不是1。有一个简单的规则来,如果一个方法被当作操作符来使用,那么它是从左到右调用,如a * b,就是a.*(b),而当方法以":"结尾时,该方法从右往左调用,如下面例子,输出结果同上面的例子,Nil是一个空列表,再其前面依次添加3,2,1,最后形成List(1,2,3),":::"也是从右向左调用。
val oneTwoThree = 1 :: 2 :: 3 :: Nil
println(oneTwoThree)一些List的方法和应用
| What is it? | What it does? |
|---|---|
| List() or Nil | The empty List |
| List("Cool", "tools", "rule") | Creates a new List[String] with the three values "Cool", "tools", and "rule" |
| val thrill = "Will" :: "fill" :: "until" :: Nil | Creates a new List[String] with the three values "Will", "fill", and "until" |
| List("a", "b") ::: List("c", "d") | Concatenates two lists (returns a new List[String] with values "a", "b", "c", and "d") |
| thrill(2) | Returns the element at index 2 (zero based) of the thrill list (returns "until") |
| thrill.count(s => s.length == 4) | Counts the number of string elements in thrill that have length 4 (returns 2) |
| thrill.drop(2) | Returns the thrill list without its first 2 elements (returns List("until")) |
| thrill.dropRight(2) | Returns the thrill list without its rightmost 2 elements (returns List("Will")) |
| thrill.exists(s => s == "until") | Determines whether a string element exists in thrill that has the value "until" (returns true) |
| thrill.filter(s => s.length == 4) | Returns a list of all elements, in order, of the thrill list that have length 4 (returns List("Will", "fill")) |
| thrill.forall(s => s.endsWith("l")) | Indicates whether all elements in the thrill list end with the letter "l" (returns true) |
| thrill.foreach(s => print(s)) | Executes the print statement on each of the strings in the thrill list (prints "Willfilluntil") |
| thrill.foreach(print) | Same as the previous, but more concise (also prints "Willfilluntil") |
| thrill.head | Returns the first element in the thrill list (returns "Will") |
| thrill.init | Returns a list of all but the last element in the thrill list (returns List("Will", "fill")) |
| thrill.isEmpty | Indicates whether the thrill list is empty (returns false) |
| thrill.last | Returns the last element in the thrill list (returns "until") |
| thrill.length | Returns the number of elements in the thrill list (returns 3) |
| thrill.map(s => s + "y") | Returns a list resulting from adding a "y" to each string element in the thrill list (returns List("Willy", "filly", "untily")) |
| thrill.mkString(", ") | Makes a string with the elements of the list (returns "Will, fill, until") |
| thrill.remove(s => s.length == 4) | Returns a list of all elements, in order, of the thrill list except those that have length 4 (returns List("until")) |
| thrill.reverse | Returns a list containing all elements of the thrill list in reverse order (returns List("until", "fill", "Will")) |
| thrill.sort((s, t) => s.charAt(0).toLowerCase < t.charAt(0).toLowerCase) | Returns a list containing all elements of the thrill list in alphabetical order of the first character lowercased (returns List("fill", "until", "Will")) |
| thrill.tail | Returns the thrill list minus its first element (returns List("fill", "until")) |
元组(Tuple)
另外一个有用的容器类型是元组tuple。类似于List,元组是不可变的,但和List不同的是Tuple可以存放不同类型的数据,而List[Int]只能存放Int类型的数据,Tuple在你希望在一个方法中返回多个值时,比较有用。在Java中,如果你希望返回多个值,你可能会创建一个Java Bean来保存这几个值,然后方法返回该Bean,但在Scala中你只需要使用Tuple即可,一旦创建了Tuple,你可以使用基于1的顺序索引访问Tuple的元素。
val pair = (99, "Luftballons")
println(pair._1)
println(pair._2)第一行构建了一个Tuple,它有两个元素,一个是Int类型的99,一个String类型的Luftballons,第二行代码中的“.”与方法调用和属性访问时的”.”一致,_1和_2是元组pair的两个字段,你可以通过pairt._1的形式来访问对应字段。一个Tuple的类型实际上取决于元素的个数和元素的类型,(99, “Luftballons”)的实际类型是Tuple2[Int, String],另外,这里访问元组元素的方式不和List一样,采用list(7),是由于apply方法总是返回相同类型的值,而元组内的元素类型不一致,所以采用了字段访问的方式。
Set and Map
Case Classes
当定义一个case class时,scala编译器会自动做以下事情:
- 构造方法中得每一个参数都会默认被声明为
val, 除非你显式声明为var(不建议) apply方法自动被添加到伴生对象中,这样,你可以使用People("mengke", "M")来构建实例,而不必使用new- 提供一个
unapply方法用于pattern match - 提供
toString,equals,hashCode以及copy方法