Scala知识点汇总

Scala数组小结

1.定长数组

定长数组:指长度不可变的数组Array。
第一种方式:
先声明一个数组,后初始化该数组:javascript

scala> val array = new Array[Double](5)
array: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0)

赋值方式:array(index) = value
第二种方式:php

scala> val array = Array(1, 2, 3, 4, 5)
array: Array[Int] = Array(1, 2, 3, 4, 5)

第三种方式,Array.fill(length)(value):html

scala> val array = Array.fill(5)(3.5)
array: Array[Double] = Array(3.5, 3.5, 3.5, 3.5, 3.5)

若是fill第二个参数只写一个值的话,那么该数组的全部元素都是该值,可是若是第二个参数是一个iterator或者random,那么数组就会被赋值为它们的值。java

scala> val array = Array.fill(2)(math.random)
array: Array[Double] = Array(0.2810736748034083, 0.7261142068882558)

第四种方式,ofDim[T](length):python

scala> val array = Array.ofDim[Double](5)
array: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0)

赋值方式:array(index) = valuenginx

2.变长数组

变长数组:指数组长度能够变化。
声明一个空的数组,此时数组没有任何元素,且不能如下标索引赋值:数组

val array = new ArrayBuffer[T]()

在数组尾部增长一个类型为T的元素e1:ruby

array += e1

在数组尾部增长一个类型为T的元素e一、e2:markdown

array += (e1, e2)

在数组尾部增长一个类型为T的数组:闭包

array ++= Array(e1, e2)

在第index元素后插入e1:

array.insert(index, e1)

在第index元素后插入e一、e2:

array.insert(index, e1, e2)

移除第index元素后的n个元素:

array.remove(index, n)

3多维数组

3.1定长多维数组

第一种方式:

val array = new Array[Array[Int]](5) 
scala> val array = new Array[Array[Int]](5)
array: Array[Array[Int]] = Array(null, null, null, null, null)

此时,内部的每一个数组长度是不同的,相似于Java中的以下定义:

int[][] array = new int[2][];

第二种方式(推荐):
用ofDim[T](rows, column, height,...)函数定义,但最多能够定义五维数组,不过已经够用了。

scala> val array = Array.ofDim[Int](2,3)
array: Array[Array[Int]] = Array(Array(0, 0, 0), Array(0, 0, 0))

 val dimArray = Array.tabulate(3, 4)((a, b) => (a + 1, b + 2))
    dimArray.foreach(println)
    for (i <- 0 until 3;j<-0 until 4)
      println(dimArray(i)(j))

 

3.2变长多维数组

scala> val arr1 = new ArrayBuffer[ArrayBuffer[Int]]()
arr1: scala.collection.mutable.ArrayBuffer[scala.collection.mutable.ArrayBuffer[Int]] = ArrayBuffer()

4.定长数组和变长数组的转换

定长数组imarray转换成变长数组array:

array = imarray.toBuffer

变长数组array转换成定长数组imarray:

imarray = array.toArray

当定/变长多维数组相互转换时,只转换最外面那层数组:

scala> val array = Array(Array(1,2),Array(3,4))
arr: Array[Array[Int]] = Array(Array(1, 2), Array(3, 4))

scala> array.toBuffer
res14: scala.collection.mutable.Buffer[Array[Int]] = ArrayBuffer(Array(1, 2), Array(3, 4))


scala for yield 的用法

 Scala中的yield的主要做用是记住每次迭代中的有关值,并逐一存入到一个数组中。
用法以下:
for {子句} yield {变量或表达式}*/
case class User(name:String,age:Int,sex:String)
object test{
def foo(n:Int,v:Int)={
for(i<-0 until n;
j<- i until n if i+j==v)
print(s"($i, $j)")

}
def main(args: Array[String]): Unit = {
val users=List(new User("gmy",25,"male"),new User("ggh",22,"female"),new User("wss",25,"female"),new User("zan",23,"female"),new User("wy",23,"female"))
val yeildUser=for(user<-users if user.sex.equals("male")||user.age<23)
yield user.name //生成列表
yeildUser.foreach(user=>println(user)) //gmy ggh
println(foo(10,10)) //(1, 9)(2, 8)(3, 7)(4, 6)(5, 5)()
}
}

 

 1 case class User(name:String,age:Int,sex:String)
 2 object YielldFor {
 3     def foo(n:Int,v:Int)={
 4       for(i<-0 until n;
 5           j<- i until n if i+j==v)
 6         print(s"($i, $j)")
 7 
 8     }
 9 
10   def foo1(n:Int,v:Int)={
11     for(i<-0 until n;
12         j<- i until v )
13       print(s"($i, $j)")
14 
15   }
16 
17   def foo2(n:Int,v:Int)={
18     for(i<-0 to n;
19         j<- i to v )
20       print(s"($i, $j)")
21 
22   }
23     def main(args: Array[String]): Unit = {
24       val users=List(new User("gmy",25,"male"),new User("ggh",22,"female"),new User("wss",25,"female"),new User("zan",23,"female"),new User("wy",23,"female"))
25       val yeildUser=for(user<-users if user.sex.equals("male")||user.age<23)
26         yield user.name   //生成列表
27       yeildUser.foreach(user=>println(user))  //gmy   ggh
28       println(foo(10,10))   //(1, 9)(2, 8)(3, 7)(4, 6)(5, 5)()
29       println(foo1(2,5))
30       println(foo2(10,5))
31     }
32 
33 }

scala中的apply方法与unapply方法

1.apply方法

当scala中类或者对象有一个主要用途的时候,apply方法就是一个很好地语法糖。

请看下面一个简单的例子:

class Foo(foo: String) { } object Foo { def apply(foo: String) : Foo = { new Foo(foo) } }

 

定义了一个Foo类,而且在这个类中,有一个伴生对象Foo,里面定义了apply方法。有了这个apply方法之后,咱们在调用这个Foo类的时候,用函数的方式来调用:

object Client {

    def main(args: Array[String]): Unit = { val foo = Foo("Hello") } }

 

咱们用Foo("Hello")的方式,就获得了一个Foo类型的对象,这一切就是apply方法的功劳。若是没有apply方法,咱们将须要使用new关键字来获得Foo对象。

2.apply方法用来作工厂

apply方法的最佳实践方式之一就是用来作工厂。好比在Scala的标准库中,许多集合类给咱们提供了apply方法来建立集合:

object Client {

    def main(args: Array[String]): Unit = { val arr = new Array[Int](3) arr(0) = 0 arr(1) = 1 arr(2) = 2 arr.foreach(x => print(x + " ")) println() val array = Array(1,2,3) array.foreach(x => print(x + " ")) } }

 

上面两种方式咱们均可以用来建立Array。第一种方式是使用new关键字,这是传统的面向对象的方式。那么第二种方式是什么状况呢?若是咱们在IDE里点进去,能够发现IDE会提示咱们有一个apply方法。点进去看apply方法的源码:

/** Creates an array of `Int` objects */ // Subject to a compiler optimization in Cleanup, see above. def apply(x: Int, xs: Int*): Array[Int] = { val array = new Array[Int](xs.length + 1) array(0) = x var i = 1 for (x <- xs.iterator) { array(i) = x; i += 1 } array }

 

 

3.unapply方法

从上面的例子不难看出,apply方法有点相似于java中的构造函数,接受构造参数变成一个对象。那么unapply方法就恰好相反,他是接受一个对象,从对象中提取出相应的值。 
unapply方法主要用于模式匹配中。 
看个简单的例子:

class Money(val value: Double, val country: String) {} object Money { def apply(value: Double, country: String) : Money = new Money(value, country) def unapply(money: Money): Option[(Double, String)] = { if(money == null) { None } else { Some(money.value, money.country) } } }

客户端实现:

def testUnapply() = {
        val money = Money(10.1, "RMB") money match { case Money(num, "RMB") => println("RMB: " + num) case _ => println("Not RMB!") } }

 

 

最后输出为:

RMB: 10.1

Scala中闭包

在Scala中,函数引入传入的参数是再正常不过的事情了,好比
(x: Int) => x > 0中,惟一在函数体x > 0中用到的变量是x,即这个函数的惟一参数。

除此以外,Scala还支持引用其余地方定义的变量:
(x: Int) => x + more,这个函数将more也做为入参,不过这个参数是哪里来的?从这个函数的角度来看,more是一个自由变量,由于函数字面量自己并无给more赋予任何含义。相反,x是一个绑定变量,由于它在该函数的上下文里有明确的定义:它被定义为该函数的惟一参数。若是单独使用这个函数字面量,而没有在任何处于做用域内的地方定义more,编译器将报错:

scala> (x: Int) => x + more <console>:12: error: not found: value more (x: Int) => x + more 

另外一方面,只要能找到名为more的变量,一样的函数字面量就能正常工做:

scala> var more = 1 more: Int = 1 scala> val addMore = (x: Int) => x + more addMore: Int => Int = $$Lambda$1104/583744857@33e4b9c4 scala> addMore(10) res0: Int = 11 

运行时从这个函数字面量建立出来的函数值(对象)被称为闭包。该名称源于“捕获”其自由变量从而“闭合”该函数字面量的动做。没有自由变量的函数字面量,好比(x: Int) => x + 1,称为闭合语(这里的语指的是一段源代码)。所以,运行时从这个函数字面量建立出来的函数值严格来讲并非一个闭包,由于(x: Int) => x + 1按照目前这个写法已是闭合的了。而运行时从任何带有自由变量的函数字面量,好比(x: Int) => x + more建立的函数,按照定义,要求捕获到它的自由变量more的绑定。相应的函数值结果(包含指向被捕获的more变量的引用)就被称为闭包,由于函数值是经过闭合这个开放语的动做产生的。

这个例子带来一个问题:若是more在闭包建立之后被改变会发生什么?在Scala中,答案是闭包可以看到这个改变,参考下面的例子:

scala> more = 9999
more: Int = 9999

scala> addMore(10)
res1: Int = 10009

很符合直觉的是,Scala的闭包捕获的是变量自己,而不是变量引用的值。正如前面示例所展现的,为(x: Int) => x + more建立的闭包可以看到闭包外对more的修改。反过来也是成立的:闭包对捕获到的变量的修改也能在闭包外被看到。参考下面的例子:

scala> val someNumbers = List(-11, -10, -5, 0, 5, 10) someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10) scala> var sum = 0 sum: Int = 0 scala> someNumbers.foreach(sum += _) scala> sum res3: Int = -11 

这个例子经过遍历的方式来对List中的数字求和。sum这个变量位于函数字面量sum += _的外围做用域,这个函数将数字加给sum。虽然运行时是这个闭包对sum进行的修改,最终的结果-11仍然能被闭包外部看到。

那么,若是一个闭包访问了某个随着程序运行会产生多个副本的变量会如何呢?例如,若是一个闭包使用了某个函数的局部变量,而这个函数又被调用了屡次,会怎么样?闭包每次访问到的是这个变量的哪个实例呢?

答案是:闭包引用的实例是在闭包被建立时活跃的那一个。参考下面的函数,函数建立并返回more闭包的函数

def makeIncreaser(more: Int) = (x: Int) => x + more 

该函数每调用一次,就会建立一个新的闭包。每一个闭包都会访问那个在它建立时活跃的变量more

scala> val inc1 = makeIncreaser(1) inc1: Int => Int = $$Lambda$1269/1504482477@1179731c scala> val inc9999 = makeIncreaser(9999) inc9999: Int => Int = $$Lambda$1269/1504482477@2dba6013 

当调用makeIncreaser(1)时,一个捕获了more的绑定值为1的闭包就被建立并返回。同理,当调用makeIncreaser(9999)时,返回的是一个捕获了more的绑定值9999的闭包。当你将这些闭包应用到入参时,其返回结果取决于闭包建立时more的定义

scala> inc1(10)
res4: Int = 11

scala> inc9999(10)
res5: Int = 10009

这里,more是某次方法调用的入参,而方法已经返回了,不过这并无影响。Scala编译器会从新组织和安排,让被捕获的参数在堆上继续存活。这样的安排都是由编译器自动完成的,使用者并不须要关心

Scala Collection(集合)

scala中:: , +:, :+, :::, +++的区别

package test
/**
* scala中的:: , +:, :+, :::, +++, 等操做;
*/
object listTest {
def main(args: Array[String]): Unit = {
val list = List(1,2,3)
// :: 用于的是向队列的头部追加数据,产生新的列表, x::list,x就会添加到list的头部
println(4 :: list) //输出: List(4, 1, 2, 3)
// .:: 这个是list的一个方法;做用和上面的同样,把元素添加到头部位置; list.::(x);
println( list.:: (5)) //输出: List(5, 1, 2, 3)
// :+ 用于在list尾部追加元素; list :+ x;
println(list :+ 6) //输出: List(1, 2, 3, 6)
// +: 用于在list的头部添加元素;
val list2 = "A"+:"B"+:Nil //Nil Nil是一个空的List,定义为List[Nothing]
println(list2) //输出: List(A, B)
// ::: 用于链接两个List类型的集合 list ::: list2
println(list ::: list2) //输出: List(1, 2, 3, A, B)
// ++ 用于链接两个集合,list ++ list2
println(list ++ list2) //输出: List(1, 2, 3, A, B)
}
}
  • :: 该方法被称为cons,意为构造,向队列的头部追加数据,创造新的列表。用法为 x::list,其中x为加入到头部的元素,不管x是列表与否,它都只将成为新生成列表的第一个元素,也就是说新生成的列表长度为list的长度+1(btw, x::list等价于list.::(x))

  • :++: 二者的区别在于:+方法用于在尾部追加元素,+:方法用于在头部追加元素,和::很相似,可是::能够用于pattern match ,而+:则不行. 关于+::+,只要记住冒号永远靠近集合类型就OK了。

  • ++ 该方法用于链接两个集合,list1++list2

  • ::: 该方法只能用于链接两个List类型的集合

具体示例

  1. scala> "A"::"B"::Nil
  2. res0: List[String] = List(A, B)
  3. scala> "A"+:"B"+:Nil
  4. res1: List[String] = List(A, B)
  5.   scala> Nil:+"A":+"B"
  6. res2: List[String] = List(A, B)
     
  7. scala> res0 ++ res1
  8. res3: List[String] = List(A, B, A, B)
     
  9. scala> res0 ::: res1
  10. res4: List[String] = List(A, B, A, B)
  11. scala> res0 :: res1
  12. res5: List[java.io.Serializable] = List(List(A, B), A, B)
引用

Scala程序设计(第2版)

scala字符串前加s使用$

 

https://my.oschina.net/u/2000675/blog/1592140

字符串中的变量替换,Scala中基础的字符串插值就是在字符串前加字幕‘s’,而后在字符串中放入变量,每一个变量都应以‘$’开头。字符串前加字母‘s’时,实际上是在建立一个处理字符串字面量

 

package demo object Demo12 { def main(args:Array[String])={ var name = "zhangsan" var age = 15 println(s"name=$name,age=$age") } }

结果

name=zhangsan,age=15 

在字符串字面量中使用表达式,“${}内可嵌入任何表达式”,包括等号表达式。

package demo object Demo12 { def main(args:Array[String])={ var name = "zhangsan" var age = 15 println(s"name=$name,age=$age") println(s"name=$name,age=${age+1}") } }

结果

name=zhangsan,age=16 

printf格式化

package demo object Demo12 { def main(args:Array[String])={ var name = "zhangsan" var age = 15 println(s"name=$name,age=$age") println(s"name=$name,age=${age+1}") var score = 89.5f printf(f"name=$name,age=${age+1},score=$score%.2f") } }

结果

name=zhangsan,age=15 name=zhangsan,age=16 name=zhangsan,age=16,score=89.50

 特殊符号的理解

// src/main/scala/progscala2/rounding/TryCatchArm.scala
package progscala2.rounding
import scala.language.reflectiveCalls
import scala.util.control.NonFatal
object manage {
def apply[R <: { def close():Unit }, T](resource: => R)(f: R => T) = {
var res: Option[R] = None
try {
res = Some(resource) // 只会引用"resource"一次!!
f(res.get)
} catch {
case NonFatal(ex) => println(s"Non fatal exception! $ex")
} finally {
if (res != None) {
println(s"Closing resource...")
res.get.close
}
}
}
}
object TryCatchARM {
/** Usage: scala rounding.TryCatch filename1 filename2 ... */
def main(args: Array[String]) = {
args foreach (arg => countLines(arg))
}
import scala.io.Source
def countLines(fileName: String) = {
println() // 打印空白行,以增长可读性
manage(Source.fromFile(fileName)) { source =>
val size = source.getLines.size
println(s"file $fileName has $size lines")
if (size > 20) throw new RuntimeException("Big file!")
}
}
}

manage.apply方法
manage.apply 方法声明看上去很是奇怪,为了可以理解该声明,咱们将对其进行分解。下
面再次列出了该方法的签名,咱们将分行显示方法签名,而每一行也都提供了对应的注释。

def apply[
R <: { def close():Unit }, ➊
T ] ➋
(resource: => R) ➌
(f: R => T) = {...} ➍

➊这行出现了两个新的事物。R 表示咱们将要管理的资源类型。而<: 则意味着R 属于某其余类型的子类。在本例中R 的父类型是一个包含close():Unit 方法的结构类型。为了能帮助你更直观的理解R 类型,尤为是当你以前没接触过结构化类型时,你可以认为R <: Closable 表示Closable 接口中定义了close():Unit 方法而且R 实现了Closable 接口。不过结构化类型容许咱们使用反射机制嵌入包含close():Unit 方法的任意类型(如Source 类型)。反射机制会形成许多系统开销,而结构化类型代价也较为昂贵, 所以就像后缀表达式那样,Scala 将反射列为可选特性,为了可以让编译器相信咱们知道咱们在作什么,须要在代码中添加import 语句。➋ 咱们传入了用于处理资源的匿名函数,而T 表示该匿名函数的返回值。➌ 尽管看上去像是一个声明体较为奇特的函数,但resource 事实上是一个传名参数(bynameparameter)。咱们暂且将其视为一个在调用时应省略括号的函数。➍ 最后咱们将传入第二个参数列表,其中包含了一个输入为resource、返回值类型为T的匿名函数,该匿名函数将负责处理resource。咱们再回到注释1,假设全部资源均实现了Closable 抽象,那么apply 方法声明看起来会是下面这个样子:object manage {def apply[ R <: Closable, T](resource: => R)(f: R => T) = {...}...}resource 只会在val res = Some(resource) 这行代码中被求值, 这行代码必不可少的。因为resource 的表现与函数类似,所以就像是一个会被重复调用的函数,每次引用该变量时便会对其求值。但咱们并不但愿每次引用resource 时都会执行一次Source.fromFile(fileName),由于这意味着咱们每次都会从新打开一个新的Source 实例。以后,咱们将res 值传入工做函数f 中。

相关文章
相关标签/搜索