Open justtreee opened 6 years ago
在本章中,主要学习在Scala中使用条件表达式,循环和函数。会看到Scala与其他编程语言的一个根本性差异。在Java或C++中,我们把表达式和语句(如if)看作两样不同的东西,表达式有值,而语句执行动作。在Scala中几乎所有构造出来的语法结构都有值。
scala> val x= 1
x: Int = 1
scala> val s = if(x>0) 1 else -1
s: Int = 1
这个语句与c++的? :
功能相同。但我们不能在? :
中插入语句,Scala的if/else
将Java/C++中的if/else
和? :
结合在一起。
{ }
块包含一系列表达式,其结果也是一个表达式。块中最后一个表达式就是块的值。
scala> val f={val a=2*3;val b=a*a; a+b}
f: Int = 42
scala> val name = readLine("input: ")
input: name: String = imoae12
scala> name
res0: String = imoae12
scala> printf("output: %s!", name)
output: imoae12!
输出类似C++。
Scala拥有与Java和C++相同的while和do循环:
scala> var n =2;var r=2
n: Int = 2
r: Int = 2
scala> while(n>0){
| r = r*n
| n -= 1}
scala> r
res4: Int = 4
scala> n
res5: Int = 0
但没有与C++那样的for循环结构。
scala> for (i <- 1 to 3){
| r = r*r}
scala> r
res7: Int = 65536
而是第一章出现的RichInt
类的to
方法,用1 to n
这个调用返回数字1到数字n(含)的Range
(区间)。
until
方法返回一个不包含上限的区间。
scala> val s = "hello"
s: String = hello
scala> var sum = 0
sum: Int = 0
scala> for(i <- 0 until s.length)
| sum += s(i)
scala> sum
res10: Int = 532
在本例中,事实上可以不用使用下标。直接遍历字符序列:
scala> var sum = 0
sum: Int = 0
scala> for(ch <- "hello") sum+= ch
scala> sum
res12: Int = 532
高级for循环和for推导式
scala> import scala.math._
import scala.math._
scala> def msqrt(x:Double) = if(x >= 0) sqrt(x) else "ERROR"
msqrt: (x: Double)Any
scala> msqrt(-1)
res15: Any = ERROR
scala> msqrt(4)
res16: Any = 2.0
你必须给出所有参数的类型,不过,只要函数不是递归的,就不需要指定返回类型。
对于不返回值的函数有特殊的表示法。如果函数体包含在花括号当中,但没有前面的=号,那么返回类型就是Unit
。这样的函数叫做过程。
scala> def box(s: String){
| var b = "-" * s.length + "--\n"
| println(b + "|" + s + "|\n" + b)}
box: (s: String)Unit
scala> box("hello")
-------
|hello|
-------
scala> def signum(x: Int) : Int = {
| if(x>0){
| 1}
| else if (x==0) 0
| else -1}
signum: (x: Int)Int
scala> signum(1)
res20: Int = 1
scala> signum(0)
res21: Int = 0
for (int i = 10; i >= 0; i—) System.out.println(i);
scala> def loop(){
| for (i <- 10.to(0, -1)){
| println(i)}}
loop: ()Unit
scala> loop 10 9 8 7 6 5 4 3 2 1 0
5. 编写一个过程countdown(n:Int),打印从n到0的数字。
```scala
scala> def f(n: Int){
| for( i<-n.to(0,-1))
| {println(i)}}
f: (n: Int)Unit
scala> f(3)
3
2
1
0
scala> def f(s: String){
| var r = 1
| for(i <- s){
| r *= i.toInt}
| println(r)}
f: (s: String)Unit
scala> f("abc") 941094
7. 同样是解决前一个练习的问题,但这次不使用循环。
```scala
scala> def f(s: String) = {
| var r = 1;
| s.foreach(r *= _.toInt)
| println(r)}
f: (s: String)Unit
scala> f("abc")
941094
_
,将字符串序列中的单个参数传给目标函数。scala> val numa = new Array[Int](10)
numa: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
scala> val aa = new Array[String](5)
aa: Array[String] = Array(null, null, null, null, null)
scala> val s = Array("hello","world")
s: Array[String] = Array(hello, world)
scala> s(0) = "asdasd"
scala> s
res1: Array[String] = Array(asdasd, world)
在JVM中,Scala的Array以Java数组方式实现,比如上文中的字符串数组在JVM中的类型为 java.lang.String
,Int ,Double 或其他与Java中基本类型对应的数组都是基本类型数组。
这种数组,Java有ArrayList,C++有vector,Scala中有ArrayBuffer。
import scala.collection.mutable.ArrayBuffer
scala> val b = ArrayBuffer[Int]()
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
scala> b+=1
res2: b.type = ArrayBuffer(1)
scala> b+=(1,2,3,4,5)
res3: b.type = ArrayBuffer(1, 1, 2, 3, 4, 5)
scala> b++=Array(66,66) //用++=追加任何集合
res5: b.type = ArrayBuffer(1, 1, 2, 3, 4, 5, 66, 66)
scala> b.trimEnd(5)
scala> b
res7: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 2)
在数组末尾插入移除元素是高效的,但在任意位置插入或移除元素,就不那么高效了。
scala> b.insert(1,2) //在下标1**之前**插入2
scala> b
res9: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 1, 2)
scala> b.insert(1,66,66,66,66) //插入多个元素
scala> b
res11: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 66, 66, 66, 66, 2, 1, 2)
scala> b.remove(1) //移除下标1的元素
res12: Int = 66
scala> b
res13: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 66, 66, 66, 2, 1, 2)
scala> b.remove(1,3) //移除从下标1元素开始的共3个元素
scala> b
res15: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 1, 2)
scala> for(i <- 0 until b.length)
| println(b(i))
1
2
1
2
也可以两个元素一跳:
scala> for(i <- 0 until (b.length,2))
| println(b(i))
1
1
对数组的转换动作不会修改原数组,而是创建一个新数组。
scala> val trans = for (elem <- b) yield 3*elem
trans: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 6, 3, 6)
for (...) yield
循环创建了一个类型与原始集合相同的新集合。如果从数组除法,那么得到的是另一个数组,如果是从数组缓冲除法,那么的到的也是一个数组缓冲。
还可增加特定条件:
scala> val trans2 = for(elem <- b if elem%2==0) yield 3*elem
trans2: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(6, 6)
求和:使用sum方法,但元素类型必须是数值类型。
scala> b.sum
res19: Int = 6
排序:
scala> val b = ArrayBuffer(1,9,2,5,3)
scala> val bsorted = b.sortWith(_ < _)
bsorted: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 5, 9)
scala> val a = Array(1,6,3,8,4)
a: Array[Int] = Array(1, 6, 3, 8, 4)
scala> scala.util.Sorting.quickSort(a) //快速排序
scala> a
res26: Array[Int] = Array(1, 3, 4, 6, 8)
举例来说,Int的二维数组类型为 Array[Array[Int]]
。要构造这样的数组,可以用ofDIm
方法:
scala> val mat = Array.ofDim[Int](3,4)
mat: Array[Array[Int]] = Array(Array(0, 0, 0, 0), Array(0, 0, 0, 0), Array(0, 0, 0, 0))
scala> mat(1)(2) = 66
scala> mat
res28: Array[Array[Int]] = Array(Array(0, 0, 0, 0), Array(0, 0, 66, 0), Array(0, 0, 0, 0))
也可以创建不规则的数组,每一行的长度不一样:
scala> val tri = new Array[Array[Int]](10)
tri: Array[Array[Int]] = Array(null, null, null, null, null, null, null, null, null, null)
scala> for(i <- 0 until tri.length)
| tri(i) = new Array[Int](i+1)
scala> tri
res30: Array[Array[Int]] = Array(Array(0), Array(0, 0), Array(0, 0, 0), Array(0, 0, 0, 0), Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
scala> def f(n: Int): Array[Int] = {
| val a = new Array[Int](n)
| val rand = new scala.util.Random()
| for(i <- a) yield rand.nextInt(n)
| }
f: (n: Int)Array[Int]
scala> f(6)
res0: Array[Int] = Array(5, 4, 0, 1, 2, 3)
for (...) yield
语句复制并返回了一个数组,对应第一行的 Array[Int]
。
scala> val a = Array(1,2,3,4,5)
a: Array[Int] = Array(1, 2, 3, 4, 5)
scala> def f(a: Array[Int]) = {
| for(i <- 0 until (a.length-1, 2)){
| val t = a(i)
| a(i) = a(i+1)
| a(i+1) = t}}
f: (a: Array[Int])Unit
scala> f(a)
scala> a
res2: Array[Int] = Array(2, 1, 4, 3, 5)
scala> def f(a: Array[Int]) = {
| for(i <- 0 until a.length) yield{
| if(i < a.length-1 && i%2 == 0){
| val t = a(i)
| a(i) = a(i+1)
| a(i+1) = t
| }
| a(i) //将最后一个没有纳入if的元素返回给for/yield产生的新数组。
| }
| }
f: (a: Array[Int])scala.collection.immutable.IndexedSeq[Int]
scala> f(a)
res4: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 1, 4, 3, 5)
现在转到intelliJ IDEA
上进行编写
import scala.collection.mutable.ArrayBuffer
val a = Array(-1,1,0,-2,3,0)
def f(a: Array[Int]) = {
val buf = ArrayBuffer[Int]()
buf ++= (for (i <- a if i > 0) yield i)
buf ++= (for (i <- a if i == 0) yield i)
buf ++= (for (i <- a if i < 0) yield i)
buf.toArray
}
f(a)
res0: Array[Int] = Array(1, 3, 0, 0, -1, -2)
val a = Array(-1.0, 1.3, 8.4,-11, 22)
def avg(a: Array[Double]) = {
a.sum / a.length
}
println(avg(a))
3.9400000000000004
val a = Array(3,0,-1,-3,4,8)
def f(a: Array[Int]) = {
for(i <- 0 until (a.length / 2)){
val t = a(i)
a(i) = a(a.length-1-i)
a(a.length-1-i) = t
}
}
f(a)
a
res1: Array[Int] = Array(8, 4, -3, -1, 0, 3)
对于`ArrayBuffer[Int]`:
```scala
import scala.collection.mutable.ArrayBuffer
val a = ArrayBuffer(3,0,-1,-3,4,8)
def f(a: ArrayBuffer[Int]) = {
val b = ArrayBuffer[Int]()
b ++= a.reverse
}
f(a)
res0: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(8, 4, -3, -1, 0, 3)
import scala.collection.mutable.ArrayBuffer
val a = ArrayBuffer(3,3,-1,3,4,8) val b = ArrayBuffer[Int]() b ++= a.distinct b.foreach(println)
3 -1 4 8
9. 创建一个由java.util.TimeZone.getAvailableIDs返回的时区集合,判断条件是它们在美洲,去掉”America/“前缀并排序。
```scala
def timeZoneName() = {
val a = java.util.TimeZone.getAvailableIDs()
val t = (for (i <- a if i.startsWith("America/")) yield {
i.drop("America/".length)
}
)
scala.util.Sorting.quickSort(t)
t
}
var a = timeZoneName()
a.foreach(println)
Adak
Anchorage
Anguilla
Antigua
....
第一章 基础
1、声明值和变量
声明变量:
以
val
定义的值实际上是一个常量——你无法改变它的内容。而var
就可以改变。在scala中,鼓励使用val
。 在scala中,变量或函数的类型总卸载变量或函数的后面。这是将两个值放在一起声明。
跟java不同,scala的类型(int,char等)都是类,也就是说,可以对数字执行方法。
更有意思的是:
在这里,Int值首先被转换为RichInt(具有int所不具备的便捷方法),再应用to方法。
2、算术和操作符重载
算术符号用法其实与其他语言相同,但需要注意的是这些操作符其实是方法,例如:
是如下方法的简写:
也就是说,这里的
+
是一个方法名。和java或C++相比,scala有个显著的不同:没有
++
和--
操作符,需要使用+=1
或-=1
。还有大数对象:
可见操作起来比java方便
3、调用函数和方法
调用一些函数时,需要引入特定包。 而不带参数的scala方法通常不使用圆括号。例如以下
distinct
方法,是获取字符串中不重复的字符:如果
s
是一个字符串,那么在C++中,会写s[i]
来获取第i个字符,在Java中是s.charAt(i)
,而scala是这样:可以把这种用法当作是
()
操作符的重载形式,他的背后是一个apply
方法:也就是说,
“hello”(4)
是如下语句的简写:练习
scala允许用数字去乘字符串:
res变量是val还是var?
scala> res17 = "asdasdasdas"