0
点赞
收藏
分享

微信扫一扫

Scala学习笔记


基础

集合

  • 可变集合:
  • 不可变集合:

元素操作

集合操作

集合操作解释

该操作适用的集合

col :+ ele

将元素添加到集合尾部

Seq

ele +: col

将元素添加到集合头部

Seq

col + ele

在集合尾部添加元素

Set、Map

col + (ele1, ele2)

将其他集合添加到集合的尾部

Set、Map

col - ele

将元素从集合中删除

Set、Map、ArrayBuffer

col - (ele1, ele2)

将子集合从集合中删除

Set、Map、ArrayBuffer

col1 ++ col2

将其他集合添加到集合尾部

Iterable

col2 ++: col1

将其他集合添加到集合头部

Iterable

ele :: list

将元素添加到list的头部

List

list2 ::: list1

将其他list添加到list的头部

List

list1 ::: list2

将其他list添加到list的尾部

List

set1

set2

取两个set的并集

set1 & set2

取两个set的交集

Set

set1 &~ set2

取两个set的diff

Set

col += ele

给集合添加一个元素

可变集合

col += (ele1, ele2)

给集合添加一个集合

可变集合

col ++= col2

给集合添加一个集合

可变集合

col -= ele

从集合中删除一个元素

可变集合

col -= (ele1, ele2)

从集合中删除一个子集合

可变集合

col -= col2

从集合中删除一个子集合

可变集合

ele +=: col

向集合头部添加一个元素

ArrayBuffer

col2 ++=: col

向集合头部添加一个集合

ArrayBuffer

集合的常用方法:head、last、tail、length、isEmpty、sum、max、min、count、exists、filter、filterNot、takeWhile、dropWhile、take、drop、splitAt、takeRight、dropRight、sclie、contains、startsWith、endsWith、indexOf、intersect、diff。

集合一般有两类操作:转换(transformation,将集合转换为另一个集合 )和行动(actions,或聚合,返回某些类型的值)。

case class Book(title: String, pages: Int)

val books = Seq(
Book("Future of Scala developers", 85),
Book("Parallel algorithms", 240)
)
books.maxBy(book => book.pages)
books.minBy(book => book.pages)
// filterNot
books.filter(book => book.pages >= 120)

// flatten:集合的集合,对这些集合的所有元素进行操作时
val uvwx = Seq('u', 'v', 'w', 'x')
val yz = Seq('y', 'z')
val alphabet = Seq(uvwx, yz)
alphabet.flatten

// sortBy,升序
case class Student(name: String, age: Int, score: Int)
List(
Student("a", 14, 60),
Student("b", 15, 80),
Student("a", 15, 70)
).sortBy(s => (s.age, s.score))

map 函数的逻辑是遍历集合中的元素并对每个元素调用函数。你也可以不调用任何函数,保持返回元素本身,但这样 map 无法发挥作用,因为你在映射过后得到的是同样的集合。

flatMap,功能强大,可简单理解为map+flatten

forall:检查集合中所有元素是否都符合某要求

partition:拆分,如把某个集合拆分成偶数集和奇数集

val abcd = Seq('a', 'b', 'c', 'd')
// List(A, a, B, b, C, c, D, d)
abcd.flatMap(ch => List(ch.toUpper, ch))

val numbers = Seq(3, 7, 2, 9, 6, 5, 1, 4, 2)
numbers.forall(n => n > 5)
numbers.partition(n => n % 2 == 0)

Fold,包括foldLeft 和 foldRight

val numbers = Seq(1, 2, 3, 4, 5)
// 求和
numbers.foldLeft(0)((res, n) => res + n)
// 计算字符数
val words = Seq("apple", "dog", "table")
words.foldLeft(0)((resultLength, word) => resultLength + word.length)

欧拉图(Euler Diagram)函数

val num1 = Seq(1, 2, 3, 4, 5, 6)
val num2 = Seq(4, 5, 6, 7, 8, 9)
// List(1, 2, 3)
num1.diff(num2)
// 交集
num1.intersect(num2)
// 并集不去重
num1.union(num2)
// 并集后去重
num1.union(num2).distinct

Scala学习笔记_开发语言


sliding:使用滑动窗口的方式分割列表,接受两个参数,size设置窗口大小,step设置每次滑动需要滑过的元素个数。如将列表的每10个元素分成一组:

val list = ('a' to 'z').map(_.toString)
list.sliding(10, 10).toList
// List(Vector(a, b, c, d, e, f, g, h, i, j), Vector(k, l, m, n, o, p, q, r, s, t), Vector(u, v, w, x, y, z))

函数

函数是一个值,能被传递和操作,

匿名函数,不命名的函数,匿名函数的作用域非常小,往往只在参数中使用,其作用范围即是调用该匿名函数参数的函数体。

函数值即高阶函数。闭包(closure)是函数值的特殊形式,会捕获或者绑定到在另一个作用域或上下文中定义的变量。

柯里化是指将接受两个参数的函数变成新的接受一个参数的函数的过程

函数值和闭包

扁平化传参?类型参数,通过类型参数构建类和函数、方法,使之适应不同类型的参数?

match表达式?

尾递归优化

使用递归来避免可变(状态),但是容易栈溢出,将递归转化成迭代以避免栈溢出的问题。并不是所有的递归都能够转化为迭代。只有尾递归才能转换为迭代。
尾调用优化,tail call optimization。
可以用tailrec注解标记任何函数,Scala会在编译时检查函数是否是尾递归的。如果不是,那么函数不能被优化,编译器会严格地报错。

编译器只能够检测到直接的递归,也就是说函数调用自己。如果两个函数相互调用,也就是蹦床调用(trampoline call),那么Scala就无法检测到这种递归,也不会做优化。
可以用TailRec类来避免栈溢出的问题。

类型

Scala学习笔记_xml_02

  • Any
    Any 是所有类型的超类型。其直接子类,AnyVal 是所有值类型(Int、Double) 的基础类型,并映射到 Java 中的原始类型,而 AnyRef 是所有引用类型的基础类型,映射到 Object,包含 Object 的方法。
  • Nothing
    所有类型的子类型,在支持 Scalal 类型验证机制意义重大,作为辅助类型,用于类型推断以及类型验证
  • Option
    同 Optional
  • Either
    希望返回两种不同类型的值之一,解决返回值不确定问题。如需求:一个函数(或方法)在传入不同参数时会返回不同的值。返回值是两个不相关的类型: Left 和 Right 。惯例中一般认为 Left 包含错误或无效值, Right包含正确或有效值。除了使用match case方式来获取数据,还可以分别使用​​​.right.get​​​和​​.left.get​​​方法,不过需要使用​​.isRight​​​或​​.isLeft​​先判断一下。Left或Right类型也有 filter,flatMap,foreach,get,getOrElse,map,toOption,toSeq方法,分别返回一个Option或Seq 。

逆变,协变

卫语句

注解

可以给类、方法、field、local variable、constructor / method / function parameter添加注解
扩展Annotation trait,自定义注解:

class Test(var timeout: Int) extends annotation.Annotation
@Test(timeout = 100) class myTest

如果要给类的主构造函数添加注解,需要在构造函数前添加注解,并加上一对圆括号:
​​​class Person @Unchecked() (val name: String, val age: Int)​​ 给表达式添加注解,此时需要在表达式后面加上冒号以及注解:

val scores = Map("Leo" -> 90, "Jack" -> 60)
(scores.get("Leo"): @unchecked) match { case score => println(score) }

除此之外,还可以给类型参数和变量的类型定义添加注解。

提供的注解

Scala中针对java的一些注解:

  1. volatile
    ​​​@volatile var name = "leo"​​​,轻量级的java多线程并发安全控制。在JVM中,可以有多个线程,每个线程都有自己的工作区,以及一块所有线程共享的工作区。每次一个线程拿到一个公共的变量,都需要从共享区中拷贝一个副本到自己的工作区中使用和修改。修改完以后,再在一个合适的时机,将副本的值写回到共享区中。
    这里就会出现一个多线程并发访问安全的问题。多个线程如果同时拷贝变量副本,都做了不同的修改,然后依次将副本修改的值,写回到共享区中,会依次覆盖掉之前的一些副本值,就会出现变量的值,是不符合预期的。volatile关键字修饰的变量可以保证,一个线程在从共享区获取一个变量的副本时,都会强制刷新一下这个变量的值,保证自己获取到的变量的副本值是最新的。所以,这是一种轻量级的多线程并发访问控制办法。但是也不是完全没有问题的,还是有可能会出现错误的风险。
  2. transient
    ​​​@transient var name = "leo"​​,瞬态字段,不会序列化这个字段
  3. SerialVersionUID
    @SerialVersionUID(value),标记类的序列化版本号
  4. native
    @native标注用c实现的本地方法
  5. throws
    ​​​@throws(classOf[Exception]) def test() {}​​让编译器提示类型转换的警告
  6. varargs
    ​​​@varargs def test(args: String) {}*​​标记方法接收的是变长参数
  7. BeanProperty
    @BeanProperty,标记生成JavaBean风格的getter和setter方法
  8. BooleanBeanProperty
    @BooleanBeanProperty,标记生成is风格的getter方法,用于boolean类型的field
  9. deprecated
    ​​​@deprecated(message = "")​​,让编译器提示警告
  10. unchecked
    @unchecked,给方法标记要抛出的checked异常
  11. switch
    @switch,生成跳转表
  12. specialized
    @specialized,基本类型特殊化

特质

不支持多重继承,若一个子类继承自不同的超类,不同的超类中同名成员子类中不知如何处理。即会导致菱形继承问题,

关键字trait:

  • 不能有构造函数
  • 继承多个特质使用with关键字
  • 所有Java接口都可以作为特质中scala中使用
  • 特质的成员可以是抽象的,不需要使用abstract关键字声明
  • 重写特质的抽象方法不需要给出override,但是多个特质重写同一个特质的抽象方法需要给出override
  • 对象构造时可以混入特质
  • 特质的构造顺序从左到右:超类,父特质,第一个特质,第二个特质(父特质不重复构造),类

Seq 特质有两个子特质 LinearSeq 和 IndexedSeq,并没有增加任何新的操作,只是为Seq中的一些方法提供更高效的实现: LinearSeq (线性序列)提供高效的 head 和 tail 方法,而 IndexedSeq(索引序列) 提供高效的 apply、length 和 update 方法(当集合可变时)。常用的线性序列类的集合有 ​​scala.collection.immutable.List​​​和​​scala.collection.immutable.Stream​​​。常用的索引序列类的集合有 ​​scala.Array​​​以及​​scala.collection.mutable.ArrayBuffer​​。

Actor

Actor 是保证互斥访问的活动对象。没有两个线程会同时处理同一个 Actor。天然的互斥行为,所有存储在 Actor 中的数据都自动是线程安全的——不需要任何显式的同步。如果能将一个任务有意义地分解为几个子任务,即分而治之,可使用 Actor 模型。

消息传递是Actor的核心,使用!向actor发送信息,使用receive接收信息,msg代表actor当前接收到的信息,actor往往使用模式匹配来进行处理消息。消息发送者不关心actor接收到消息之后如何处理,不期待返回。receive方法的参数有各种case语句组成, receive方法获取到消息后依次传递给其参数。通过react实现线程的复用,receive从线程池获取一个线程并一直使用, react从线程池获取一个线程,使用完释放。

actor(Scala.Actor)的主体为act方法, act方法不能被外部显式调用,actor的act方法在start后启动, act方法本身并不会自动获取线程,在act方法里只能获取一次线程,在以下情况下actor终止执行act方法:

  • act方法返回
  • act方法由于异常被禁止
  • actor调用exit方法

使用建议

  • 更多地依赖无状态的而不是有状态的 Actor。无状态的 Actor 没有特殊性,它们可以提供更多的并发性,易于复制,并且很容易重启和使用。状态可能是不可避免的,但是要尽可能少使用有状态的 Actor
  • 要保证 receive() 方法中的处理速度非常快,尤其是接收 Actor 具有状态时。改变状态的长时间运行任务会降低并发性,要避免这么做。如果不修改状态的任务不是非常快也不是什么问题,因为我们可以很容易地复制这些 Actor 来改进并发性
  • 确保在 Actor 之间传递的消息是不可变对象。保证不会在无意中修改共享状态,并最终导致并发问题。
  • 尽量避免使用 ask()。双向通行通常都不是一个好主意。『发送并忘记』模型要好得多,而且也更加不容易出错。

Akka

Scala在2.10.0版本自带Akka类库,是scala语言写的Actor模型的推荐实现,包含大量的工具去完善、辅助并发开发

import scala.actors.Actor
import akka.actor.Actor

Scala中XML

  1. 定义
    直接在Scala代码中定义一个xml文档元素:
    ​​​val books = <books><book>my first scala book</book></books>​​​ 此时doc的类型是scala.xml.Elem,也就是一个xml元素。
    还可以直接定义多个同级别的xml元素:
    ​val books = <book>my first scala book</book><book>my first spark book</book>​​ 此时doc的类型是scala.xml.NodeBuffer,也就是一个xml节点序列。
  2. XML节点类型
    Node类是所有XML节点类型的父类型,两个重要的子类型是Text和Elem。
    Elem表示一个XML元素,也就是一个XML节点。scala.xml.Elem类型的label属性返回的是标签名,child属性返回的是子元素。
    scala.xml.NodeSeq类型是一个元素序列,可以用for循环直接遍历它。
    可以通过scala.xml.NodeBuffer类型,来手动创建一个节点序列:

val booksBuffer = new scala.xml.NodeBuffer
booksBuffer += <book>book1</book>
booksBuffer += <book>book2</book>
val books: scala.xml.NodeSeq = booksBuffer

  1. xml元素的属性
    scala.xml.Elem.attributes属性,可以返回这个xml元素的属性,是Seq[scala.xml.Node]类型的,调用text属性可以拿到属性的值。

val book = <book id="1" price="10.0">book1</book>
val bookId = book.attributes("id").text
// 遍历属性
for(attr <- book.attributes) println(attr)
// 获取一个属性Map
book.attributes.asAttrMap

  1. 在xml中嵌入scala代码

val books = Array("book1", "book2")
<books><book>{ books(0) }</book><book>{ books(1) }</book></books>
<books>{ for (book <- books) yield <book>{book}</book> }</books>
// 在xml属性中嵌入scala代码
<book id={ books(0) }>{ books(0) }</book>

  1. 修改xml元素
    默认scala中的xml表达式是不可改变的,必须拷贝一份再修改。

val books = <books><book>book1</book></books>
// 添加一个子元素
import scala.xml._
val booksCopy = books.copy(child = books.child ++ <book>book2</book>)
val book = <book id="1">book1</book>
// 修改一个属性
val bookCopy = book % Attribute(null, "id", "2", Null)
// 添加一个属性
val bookCopy = book % Attribute(null, "id", "2", Attribute(null, "price", "10.0", Null))

  1. 加载和写入外部xml文件

import scala.xml._
import java.io._
val books = XML.loadFile("C://Users//aa//Desktop//books.xml")
val books = XML.load(new FileInputStream("C://Users//aa//Desktop//books.xml"))
// 使用Java的InputStreamReader类指定加载编码
val books = XML.load(new InputStreamReader(new FileInputStream("C://Users//aa//Desktop//books.xml"), "UTF-8"))
XML.save("C://Users//aa//Desktop//books2.xml", books)

Scala中JSON

实例

最长子串

测试题:给你一个String S,你需要找到包含大写和小写字符,但不包含数字的最长子字符串。比如dP4knqw1QAp,答案QAp。

def theLongest(s: String): String = {
s.split ("[0-9]")
.filter (_.exists (ch => ch.isUpper))
.filter (_.exists (ch => ch.isLower))
.maxBy (_.length)
}

如果输入字符串不包含任何合适的子字符串,将会抛出 UnsupportedOperationException。

斐波那契

scala> val fibs: Stream[Int] = 0 #:: fibs.scanLeft(1){ _+_ }
fibs: Stream[Int] = Stream(0, ?)

scala> fibs take 10 toList
res0: List[Int] = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)

// 又
scala>val input = List(3, 5, 7, 11)
scala> input.scanLeft(0)(_+_)
res0: List[Int] = List(0, 3, 8, 15, 26)

技巧

  1. match,即switch,支持管道符匹配多个分支;
  2. String的stripSuffix,stripPrefix方法,可以获得前、后半部分
  3. isAssignableFrom用来判断一个类Class1和另一个类Class2是否相同或是另一个类的超类或接口:​​Class1.isAssignableFrom (Class2)​​ instanceof 用来判断一个对象实例是否是一个类或接口的或其子类子接口的实例
  4. @transient注解将字段标记为瞬态,不会被序列化
  5. ​=>​​​符号可以看做是创建函数实例的语法糖。如:​​A,B => T​​​表示一个函数的输入参数类型是"A,B",返回值类型是T。​​() => T​​​表示函数输入参数为空,而​​A => Unit​​则表示函数没有返回值

实例

"RichString.java".stripSuffix(".java") == "RichString"
"http://my.url.com".stripPrefix("http://") == "my.url.com"

高级

Scala web框架

Finatra

Finatra是一款基于TwitterServer和Finagle的快速、可测试的Scala异步框架

Play

Play是一款轻量级、无状态的WEB友好框架。

Scalatra

Scalatra是一款简单,方便和免费的web微架构。它结合JVM的力量和Scala的美丽和简洁,帮助您快速建立高性能的网站和API。

Spray

Spray是一款开源的工具包,可以使用它构建基于Scala和Akka之上的REST/HTTP的整合层。

参考

​​Scala 实用指南读书笔记​​​​Scala程序员须知:这些技巧需手到擒来​​​​type-level-programming-in-scala/​​Scala的常用小技巧


举报

相关推荐

scala语言学习笔记

Scala学习笔记16: 注解

Scala学习笔记13: 集合

Scala学习

Scala 复习笔记

0 条评论