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(知识点汇总) 代数系统 代数系统定义 例子 二元运算定义 运算及其性质 二元运算的性质 封闭性 可交换性 可结合性 可分配性 吸收律 等幂性 消去律 ...
随机推荐
- 管理多个gradle,SDKMAN
背景:同一台机器上有两个app需要编译,但是两个app的gradle版本不一致,所以需要安装一个管理gradle版本的工具 sdkman:(Software Development Kit Man ...
- Sprite(雪碧图)的应用
雪碧图是根据CSS sprite音译过来的,是将很多很多的小图标放在一张图片上. 使用雪碧图的目的:有时为了美观,我们会使用一张图片来代替一些小图标,但是一个网页可能有很多的小图标,浏览器在显示页面的 ...
- [InstFiles]在Inno中打包隐藏和系统文件的头文件
本文来自:http://www.kngstr.com 简介: 一直以来,Inno的作者都没有提供打包隐藏文件和系统文件的功能. 但是,如果我们做批量打包,批量封装的时候,总会需要这样的功能,因为我们不 ...
- 【CSS】环形进度条
效果图 原理剖析 1.先完成这样一个半圆(这个很简单吧) 2.overflow: hidden; 3.在中间定位一个白色的圆形做遮挡 4.完成另一半 5.使用animate配合时间完成衔接 源码 &l ...
- 手工脱壳之AsPack压缩脱壳-随机基址
一.工具及壳介绍二.脱壳1.ESP定律脱壳2.单步跟踪脱壳3.基址重定位的修复 一.工具及壳介绍 使用工具:Ollydbg.PEID.ImportREC.LoadPE.010 Editor 查看待脱壳 ...
- mysql 数据库导入导出方法总结
一般形式:mysqldump -h IP -u 用户名 -p 数据库名 > 导出的文件名 (1)-p 后面不能加password,只能单独输入如1中那样 (2)mysqldump是在cmd下的命 ...
- gitlab简介与配置
版本控制介绍 版本控制是指对软件开发过程中各种程序代码.配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一. 版本控制最主要的功能就是追踪文件的变更.它将什么时候.什么人更改了文件的什么 ...
- IDEA快捷键 日常整理
F9 : debug Ctrl+” +/- ” : 当前方法展开.折叠 Ctrl+Shift+”+/-” : 全部展开.折叠 Alt+1 : 工程面板 Alt+4:控制台 Alt+7:查看本类方法 S ...
- PYTHON基础入门(内置函数、推导式)学习
**内建函数**1.通过使用dir()函数可以列出所具备的方法 例:num = 10 dir(num) 例:myList = [1,2,3,4,5,6] dir(num)2.通过使用help()函数可 ...
- 高速上手C++11 14 笔记1
1 constexpr constexpr关键字可以让已经具备常量返回的函数运用于常量的位置. c++14起可以在函数内部使用局部变量.循环和分支等简单语句. 2 委托构造&继承构造 委托构造 ...