Scala知识点汇总
Scala数组小结
1.定长数组
定长数组:指长度不可变的数组Array。
第一种方式:
先声明一个数组,后初始化该数组:
- scala> val array = new Array[Double](5)
- array: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0)
赋值方式:array(index) = value
第二种方式:
- scala> val array = Array(1, 2, 3, 4, 5)
- array: Array[Int] = Array(1, 2, 3, 4, 5)
第三种方式,Array.fill(length)(value):
- 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,那么数组就会被赋值为它们的值。
- scala> val array = Array.fill(2)(math.random)
- array: Array[Double] = Array(0.2810736748034083, 0.7261142068882558)
第四种方式,ofDim[T](length):
- scala> val array = Array.ofDim[Double](5)
- array: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0)
赋值方式:array(index) = value
2.变长数组
变长数组:指数组长度可以变化。
声明一个空的数组,此时数组没有任何元素,且不能以下标索引赋值:
- val array = new ArrayBuffer[T]()
在数组尾部增加一个类型为T的元素e1:
- array += e1
在数组尾部增加一个类型为T的元素e1、e2:
- array += (e1, e2)
在数组尾部增加一个类型为T的数组:
- array ++= Array(e1, e2)
在第index元素后插入e1:
- array.insert(index, e1)
在第index元素后插入e1、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)()
- }
- }
- case class User(name:String,age:Int,sex:String)
- object YielldFor {
- def foo(n:Int,v:Int)={
- for(i<- until n;
- j<- i until n if i+j==v)
- print(s"($i, $j)")
- }
- def foo1(n:Int,v:Int)={
- for(i<- until n;
- j<- i until v )
- print(s"($i, $j)")
- }
- def foo2(n:Int,v:Int)={
- for(i<- to n;
- j<- i to v )
- print(s"($i, $j)")
- }
- def main(args: Array[String]): Unit = {
- val users=List(new User("gmy",,"male"),new User("ggh",,"female"),new User("wss",,"female"),new User("zan",,"female"),new User("wy",,"female"))
- val yeildUser=for(user<-users if user.sex.equals("male")||user.age<)
- yield user.name //生成列表
- yeildUser.foreach(user=>println(user)) //gmy ggh
- println(foo(,)) //(, )(, )(, )(, )(, )()
- println(foo1(,))
- println(foo2(,))
- }
- }
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类型的集合
具体示例
scala> "A"::"B"::Nil
res0: List[String] = List(A, B)
scala> "A"+:"B"+:Nil
res1: List[String] = List(A, B)
scala> Nil:+"A":+"B"
res2: List[String] = List(A, B)
scala> res0 ++ res1
res3: List[String] = List(A, B, A, B)
scala> res0 ::: res1
res4: List[String] = List(A, B, A, B)
scala> res0 :: res1
res5: List[java.io.Serializable] = List(List(A, B), A, B)
引用
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 事实上是一个传名参数(byname
parameter)。我们暂且将其视为一个在调用时应省略括号的函数。
➍ 最后我们将传入第二个参数列表,其中包含了一个输入为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 中。
Scala知识点汇总的更多相关文章
- nginx几个知识点汇总
WHY? 为什么用Nginx而不用LVS? 7点理由足以说明一切:1 .高并发连接: 官方测试能够支撑 5 万并发连接,在实际生产环境中跑到 2 - 3 万并发连接数.?2 .内存消耗少: 在 3 万 ...
- python全栈开发 * 10知识点汇总 * 180612
10 函数进阶 知识点汇总 一.动态参数 形参的第三种1.动态接收位置传参 表达:*args (在参数位置编写 * 表⽰接收任意内容) (1)动态位置参数def eat(*args): print(a ...
- 清华大学OS操作系统实验lab1练习知识点汇总
lab1知识点汇总 还是有很多问题,但是我觉得我需要在查看更多资料后回来再理解,学这个也学了一周了,看了大量的资料...还是它们自己的80386手册和lab的指导手册觉得最准确,现在我就把这部分知识做 ...
- Scala 知识点掌握1
Scala知识点巩固 1.Scala简介 Scala是一门面向对象和面向函数的编程语言,是一门静态编程语言,如 Java Scala(变量类型在编译阶段确定):源码文件需要基于 JVM 运行的. 动态 ...
- c++ 函数知识点汇总
c++ 函数知识点汇总 swap函数 交换两个数组元素 比如 swap(a[i],a[j]); 就是交换a[i] 和 a[j] 的值 strcpy() 复制一个数组元素的值到另一个数组元素里 strc ...
- 前端开发 JavaScript 干货知识点汇总
很多初学的朋友经常问我,前端JavaScript都需要学习哪些东西呀?哪些是JavaScript的重点知识啊? 其实做前端开发工程师,所有的知识点都是我们学习必备的东西,只有扎实的技术基础才是高薪的关 ...
- BBS项目知识点汇总
目录 bbs项目知识点汇总 一. JavaScript 1 替换头像 2 form表单拿数据 3 form组件error信息渲染 4 添加html代码 5 聚焦操作 二 . html在线编辑器 三 . ...
- Java面试知识点汇总
Java面试知识点汇总 置顶 2019年05月07日 15:36:18 温柔的谢世杰 阅读数 21623 文章标签: 面经java 更多 分类专栏: java 面试 Java面试知识汇总 版权声明 ...
- 离散数学 II(最全面的知识点汇总)
离散数学 II(知识点汇总) 目录 离散数学 II(知识点汇总) 代数系统 代数系统定义 例子 二元运算定义 运算及其性质 二元运算的性质 封闭性 可交换性 可结合性 可分配性 吸收律 等幂性 消去律 ...
随机推荐
- python windows环境响铃
import winsound winsound.Beep(600,1000) #其中600表示声音大小,1000表示发生时长,1000为1秒
- Java基础(命令行操作、注释及API、)
一.常用的dos命令. dir:列出当前目录下的文件及文件夹 md:创建目录 rd:删除目录 cd:进入到指定目录 cd..:退出到上一级目录 cd\:退出到根目录 del:删除文件 exit:退出d ...
- 100-days: twenty-eight
Title: Lawrence Ferlinghetti's(劳伦斯·费林盖蒂) enduring San Francisco(旧金山) 劳伦斯·费林盖蒂心中的旧金山,历久弥新 费林盖蒂:美国垮掉的一 ...
- 项目总结22:Java UDP Socket数据的发送和接收
项目总结22:Java UDP Socket数据的发送和接收 1-先上demo 客户端(发送数据) package com.hs.pretest.udp; import java.io.IOExcep ...
- 控制请求重复提交的方法总结(Token)
重复提交的定义: 重复提交指的是同一个请求(请求地址和请求参数都相同)在很短的时间内多次提交至服务器,从而对服务器造成不必要的资源浪费,甚至在代码不健壮的情况还会导致程序出错. 重复提交的原因或触发事 ...
- Git实际操作
1.基本操作 git init 初始化仓库 git status 查看仓库状态 git add XXX.XX 向暂存区中添加文件XXX.XX git commit 保存仓库的历史记录 git log ...
- sftp免密登录
最近在linux服务器上搭建了一个sftp服务器,在做免密登录时,因为理解的问题,走了些弯路,在此记录一下. 先来说一下服务器端,在服务端的每一个用户的根目录下都有一个.ssh目录,在这个下面放的公私 ...
- Spring bean的生命流程
Spring 是一个轻量级的 J2EE 开源框架,其目标是降低企业级应用开发难度,提高企业级应用开发效率.在日程开发中,我们会经常使用 Spring 框架去构建应用.所以作为一个经常使用的框架,了解其 ...
- java之数据库相关
这篇还是在回顾知识.主要是关于java连接Sqlserver2012数据库的一些方式记录,以便以后查询. 十一之内复习完这些知识就可以新学Hibernate啦(*^▽^*) 1.普通方式 注意,在连接 ...
- element-ui
配合vue的前端样式组建 element-ui 1,基础布局 <el-row> <el-col :span="8"></el-col> &l ...