justtreee / blog

31 stars 8 forks source link

【Scala学习笔记 · 一】基础、控制结构和函数、数组操作 #5

Open justtreee opened 6 years ago

justtreee commented 6 years ago

第一章 基础

1、声明值和变量

声明变量:

scala> val a = 8*8
a: Int = 64
scala> var hhh = "hello world"
hhh: String = hello world

val定义的值实际上是一个常量——你无法改变它的内容。而var就可以改变。在scala中,鼓励使用val。 在scala中,变量或函数的类型总卸载变量或函数的后面。

var a,b:String = "as"

这是将两个值放在一起声明。

跟java不同,scala的类型(int,char等)都是,也就是说,可以对数字执行方法。

scala> 666.toString
res1: String = 666

更有意思的是:

scala> 2.to(10)
res4: scala.collection.immutable.Range.Inclusive = Range 2 to 10

在这里,Int值首先被转换为RichInt(具有int所不具备的便捷方法),再应用to方法。

2、算术和操作符重载

scala> val a = 1+2*3/6-1
a: Int = 1

算术符号用法其实与其他语言相同,但需要注意的是这些操作符其实是方法,例如:

a + b

是如下方法的简写:

a.+(b)

也就是说,这里的+是一个方法名。

和java或C++相比,scala有个显著的不同:没有++--操作符,需要使用+=1-=1

ans+=1

还有大数对象:

scala> val x:BigInt = 99999999
x: BigInt = 99999999

scala> x*x*x
res6: scala.math.BigInt = 999999970000000299999999

可见操作起来比java方便

3、调用函数和方法

scala> import scala.math._
import scala.math._

scala> sqrt(2)
res8: Double = 1.4142135623730951

调用一些函数时,需要引入特定包。 而不带参数的scala方法通常不使用圆括号。例如以下distinct方法,是获取字符串中不重复的字符:

scala> "ddddhello".distinct
res9: String = dhelo

如果s是一个字符串,那么在C++中,会写s[i]来获取第i个字符,在Java中是s.charAt(i),而scala是这样:

scala> var s = "hello"
s: String = hello

scala> s(4)
res10: Char = o

scala> "hello"(4)
res11: Char = o

可以把这种用法当作是()操作符的重载形式,他的背后是一个apply方法:

def apply(n: Int): Char

也就是说,“hello”(4)是如下语句的简写:

"hello".apply(4)

练习

  1. scala允许用数字去乘字符串:

    scala> "crazy"*3
    res15: String = crazycrazycrazy
  2. res变量是val还是var?

    
    scala> res15+"asd"
    res17: String = crazycrazycrazyasd

scala> res17 = "asdasdasdas"

:15: error: reassignment to val res17 = "asdasdasdas" ``` 答:res变量是val,一个不可改变的常量。 3. 用BigInt计算2的1024次方。 ```scala scala> val x:BigInt = 2 x: scala.math.BigInt = 2 scala> x.pow(1024) res22: scala.math.BigInt = 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216 ``` 4. 在Scala中如何获取字符串的首字符和尾字符? ```scala scala> val a="123456789" a: String = 123456789 scala> a.head res24: Char = 1 scala> a.last res25: Char = 9 ``` 5. take, drop, takeRight, dropRight这些字符串函数是做什么用的?和substring相比,它们的优点和缺点都有哪些? ```scala scala> val a="123456789" a: String = 123456789 scala> a.take(2) res26: String = 12 scala> a.drop(2) res27: String = 3456789 scala> a.takeRight(2) res28: String = 89 scala> a.dropRight(2) res29: String = 1234567 ```
justtreee commented 6 years ago

第二章 控制结构和函数

在本章中,主要学习在Scala中使用条件表达式,循环和函数。会看到Scala与其他编程语言的一个根本性差异。在Java或C++中,我们把表达式和语句(如if)看作两样不同的东西,表达式有值,而语句执行动作。在Scala中几乎所有构造出来的语法结构都有值。

1、 条件表达式

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? :结合在一起。

1 2

2. 块表达式和赋值

{ }块包含一系列表达式,其结果也是一个表达式。块中最后一个表达式就是块的值。

scala> val f={val a=2*3;val b=a*a; a+b}
f: Int = 42

3. 输入和输出

scala> val name = readLine("input: ")
input: name: String = imoae12

scala> name
res0: String = imoae12

scala> printf("output: %s!", name)
output: imoae12!

输出类似C++。

4. 循环

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推导式

3

5. 函数

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

你必须给出所有参数的类型,不过,只要函数不是递归的,就不需要指定返回类型。

6. 过程

对于不返回值的函数有特殊的表示法。如果函数体包含在花括号当中,但没有前面的=号,那么返回类型就是Unit。这样的函数叫做过程

scala> def box(s: String){
     | var b = "-" * s.length + "--\n"
     | println(b + "|" + s + "|\n" + b)}
box: (s: String)Unit

scala> box("hello")
-------
|hello|
-------

7. 懒值与异常

l1 l2

e1

练习

  1. 一个数字如果为正数,则它的signum为1;如果是负数,则signum为-1;如果是0,则signum为0.编写一个函数来计算这个值。
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
  1. 一个空的块表达式{}的值是什么?类型是什么?
  1. 指出在Scala中何种情况下赋值语句x = y = 1是合法的。(提示:给x找个合适的类型定义。)
  1. 针对下列Java循环编写一个scala版, 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
  1. 编写一个for循环,计算字符串中所有字母的Unicode代码的乘积。
    
    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
justtreee commented 6 years ago

第三章 数组相关操作

1. 定长数组

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中基本类型对应的数组都是基本类型数组。

2. 变长数组:数组缓冲

这种数组,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)

3. 遍历数组和数组缓冲

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

4. 数组转换

对数组的转换动作不会修改原数组,而是创建一个新数组。

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)

5. 常用算法

求和:使用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)

6. 多维数组

举例来说,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))

练习

  1. 编写一段代码,将a设置为一个n个随机整数的数组,要求随机数介于0和n之间。
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]

  1. 编写一个循环,将整数数组中相邻的元素置换。
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)
  1. 重复前一个练习,不过这次生成一个新的值交换过的数组。用for/yield。
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)
  1. 给定一个整数数组,产出一个新的数组,包含元数组中的所有正值,以原有顺序排列,之后的元素是所有零或负值,以原有顺序排列。

现在转到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)
  1. 如何计算Array[Double]的平均值?
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
  1. 如何重新组织Array[Int]的元素将它们反序排列?对于ArrayBuffer[Int]你又会怎么做呢?
    
    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)
  1. 编写一段代码,产出数组中的所有值,去掉重复项。
    
    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
....