Scala语法简摘

栏目: Scala · 发布时间: 6年前

内容简介:本文摘录Scala语言的一些语法和关键概念,不成系统,可看做学习笔记罢。Scala程序员的平衡感:

本文摘录Scala语言的一些语法和关键概念,不成系统,可看做学习笔记罢。

类型推断

for (arg <- args)arg 一定是val类型,循环中不能改变其值。

Scala程序员的平衡感:

  • 崇尚val,不可变对象和没有副作用的方法
  • 首先想到他们,只有在特定需要或权衡后才选择var,可变对象或者带副作用方法。

Scala 伴生对象,是一个单例对象,可以看做 Java 中可能用到的静态方法 工具

基本类型

任何方法都可以是操作符,任何操作符都是方法。

函数式对象

辅助构造器,关键词this指向当前执行方法被调用的对象实例

如果使用在构造器里的话,就是指正在构建的实例

辅助构造器使用 def this(..) 定义,每个Scala构造器调用终将结束于对主构造器的调用。因为主构造器是类的唯一入口点。

重载操作符,重载后仍然按照原来的优先级,比如* > +

字面量标识符 yield 可以作为一个变量/常量名

函数和闭包

本地函数:函数定义在函数中,本地函数可以随意访问包含它的函数的参数

函数字面量

例子: (x: Int) => x + 1
foreachfilter 等许多函数中会使用到,x的类型往往可以被推断,所以通常也可写成: x => x + 1

函数字面量存在于源代码,而函数值作为对象存在于运行期。

更简单的,可以使用占位符语法,用下划线当做一个或者多个参数的占位符,只要每个参数在函数字面量内只出现一次即可,第n个下划线代表第n个参数

filter(_ > 0) ,调用时,用参数来填补下划线,也即 filter(x > 0)

reduce(_ + _) ,调用时,分别填补,也即 reduce(l+r)

偏函数(部分应用函数),一个下划线代替所有参数

闭包:函数字面量中包含了自由变量的绑定,运行时必须捕获其绑定。

val addMore = (x: Int) => x + moremore 是自由变量

注意,闭包是一个非常重要的概念,我们时常会想要在循环体,比如foreach,map中加入一些对外部变量的修改,这是我们在其他语言养成的习惯。

直觉上,Scala在运行时会捕获自由变量本身,而不是变量指向的值。

比如

(x: Int) => x + more

此时创建的闭包可以看到闭包外部对more的改变,同样,闭包对捕获变量做出的修改在闭包外部也可见,比如:

val someNumber = List(-11, -10, 0, 10)
var sum = 0
someNumber.foreach(x => sum += x)

scala> sum
res: Int = -11

但是如果读者用过Spark的话,一定会了解到Spark的闭包和Scala的闭包是不一样的,原因就在于Spark是分布式环境下运行的。

这里 有Spark官方对closure的描述。

还是上面那个例子

var sum = 0
var rdd = sc.parallelize(someNumbers)

// Wrong: Don't do this!!
rdd.foreach(x => sum += x)

println("Counter value: " + counter)

// counter = 0, because driver cannot feel the change of counter

众所周知,在分布式环境下,rdd的操作形成一个闭包,闭包会先序列化,然后被调度到各个executor执行,且每个executor拿到的其实是序列化后的sum,相当于driver端的一个copy,executor对sum的操作对driver来说不可见,driver的sum对各个executor来说也不可见,所以在driver端,counter始终是0。这就是scala闭包和spark闭包概念的一个最大不同。

重复参数,即可变参数,尾部加 * 号即可,如:

def echo(args: String*)

args 其实是 Array[String] 类型,但是仍然不能真的传入一个 Array[String] 类型的参数,比如要传入 arr ,你需要 echo(arr: _*) 这么写,意思是告诉编译器把每个元素当参数而不是把arr当做单一参数。

尾递归:在最后一个动作调用自己的函数。注意只能是单纯的调用自己,不能有多余的表达式,也不能通过其它函数中转

Scala的核心:简洁,简洁,简洁!

控制抽象

柯里化,传名参数

组合与继承

组合指一个类持有另一个的引用,借助被引用的类完成任务。

不带参数,且没有副作用的方法可以不写括号

“脆基类”问题:意外的方法重写

多态的重新理解:父类型引用可以指向子类型对象 => 父类对象可有多种形式 => 多态

动态绑定:被调用的实际方法取决于运行期对象基于的类型

Scala 层级

所有类的父类是Any类,下辖两个子类,AnyRef(所有引用类的父类)和AnyVal(所有值类的父类)

底层有Nothing类和Null类,Null类是所有引用类的子类,不兼容子类型,而Nothing是所有类的子类。

scala的==对值类型为自然相等,对引用类型来说被视为equals方法的别名,equals初始定义为引用相等,但许多子类都会重写它以实现自然意义上的相等。

要比较引用相等,可以使用eq方法(反面是ne方法)

特质(trait)

特质类似Java中的接口,混入特质可以使用extends或者with

特质像是带有具体方法的Java接口,并且可以声明字段和维持状态值,特质可以做类定义所能做的事

但与类定义有两点不同:

1) 特质不能有参数(传递给主构造器)

2)super调用时动态绑定的

胖接口:拥有更多方法的接口

特质的一个用法就是把瘦接口变成胖接口

需要 排序 比较时,可以混入(mixin)Ordered特质

步骤:混入Ordered特质,实现compare方法,可以自动拥有大多数比较方法,但是不会有equals方法 => 类型擦除

特质的第二个用法:为类提供可堆叠的改变

混入多个特质,最右边的特质最先起作用

不同的组合,不同的次序混入特质,可以依靠少量的特质得到多个不同的类

特质线性化地解释super

特质,用还是不用?

1) 如果行为不会被重用,那做成具体类

2)如果要在多个不相关的类中重用,那就做成特质

3)如果希望从Java代码继承,那就是用抽象类 (只含有抽象成员的scala特质会被直接翻译成Java接口)

4)如果计划以编译后的方式发布,或者希望外部组织继承它,更倾向使用抽象类

5)如果效率很重要,倾向于使用类

包和引用

_root_ 顶层包:所有你能写出来的顶层包都是 _root_ 的成员,可以用 _root_.yourpack 来访问

scala应用灵活在于:

1)可以随处import

2)可以指对象或包

3)可以重命名或者隐藏

每个scala源文件都隐含引用java.lang包,scala包以及单例对象Predef

访问修饰符:protected比Java中的更加严格:仅限子类访问,同一包中的类不能访问

访问修饰符限定规则:

private[X] method/class 此类或方法对X下所有类和对象可见

protected[X] method/class 对此类或子类或修饰符所在的包,类或对象X可见 (?)

断言和单元测试

assert :

assert(ele.width === 2) 三等号,如果不等,会报告 “3 dit not equal to 2”

expect (2) {
  ele.width
}

intercept检查是否抛出了期待的异常

intercept(class[IllegalArgumentException]) {
  elem('x', -2, 3)
}

scala中的一些测试方法:

1) ScalaTest

2) Suite:

3) JUnit

4) TestNG

5) Specs 规格测试, a should be … 具有描述部分和规格部分

ScalaCheck 属性测试,测试代码具有属性

样例类和模式匹配

样例类 case class CLSNAME(argA: argAType, ...)
最大的好处是他们可以支持模式匹配

模式有很多种,包括通配,常量模式,变量模式,构造器模式,序列模式,元组模式,类型模式,更高级的还有变量绑定

使用类型模式应注意类型擦除,擦除规则不适用于数组

编译器会为 case class 自动生成伴生对象

编译器也会为该伴生对象自动生成 apply , unapply 方法

模式守卫

列表:List

List是协变的,意味着,如果S是T的子类,List[S]就是List[T]的子类,故而 List[Nothing]List[String] 的子类,故可以 val List[String] = List()

:: 元素与List连接,元素与元素连接

::: List与List连接

计算长度 .length 方法需要遍历整个列表,所以如果判断长度为0的话最好使用 .isEmpty 方法

访问头部: init 方法,访问除了最后一个元素外的子列表, head 方法:访问第一个元素

访问尾部: last 方法,访问最后一个元素, tail 方法:访问除第一个元素外的列表

更一般的, drop , take 方法

copyToArray : 把列表元素复制到目标数组的一段连续空间

elements 方法:返回迭代器

其他的List高阶方法:

map,flatMap,foreach
过滤:filter,partition,find,takeWhile,dropWhile,span
论断:forall,exists
折叠:/: 和 :\
翻转reverse,排序sortWith

List对象的方法:

List.apply,List.range,List.make(4, 'a')
List.unzip 解除啮合
连接: List.flatten, List.concat

区别在于前者用列表的列表做参数,后者可以直接用多个列表作为参数(以可变参数的方式)

Scala类型推断

Scala采用局部的,基于流的类型推断算法

通常,一旦有需要推断多态方法类型参数的任务时,类型推断器只会参考第一个参数列表中所有的值参数类型,而不会参考之后的参数。

库方法设计原则:

如果需要把参数设计为若干非函数值即一个函数值的某种多态方法,需要把函数参数独自放在柯里化参数列表的最后面。

即,在柯里化方法中,方法类型仅取决于第一段参数。

同样的,一种快速解决类型错误问题的方法:

添加明确的类型标注

集合与映射

Set, Seq, Map -> Iterable
SortedSet
SynchronizedMap

如果元素数量不多,不可变集合比可变集合存储更紧凑,空间更加节省。

可变状态的对象

状态与var变量常常一起出现,但并不具有严格的关系。

类即使没有定义或继承var变量,也可以由于把方法调用传递给其他具有可变状态的对象而带有状态,(有点拗口)

类即使包含了var变量也可以仍是纯函数的

Scala中,对象的每个非私有的var类型成员变量都隐含定义了 gettersetter 方法。

getter 方法为 x

setter 方法为 x_

类中字段初始化为0/false/null,

var x = _

不可以省略 “= _”,否则var x: Float 为抽象变量,而不是未初始化的变量

类型参数化(重头戏)

类型参数化让我们能够编写泛型和特质

信息隐藏:

隐藏主构造器: private加载类名的后面,类参数列表的前面:

class Queue[T] private (
  private val leading: List[T],
  private val tailing: List[T]
)

那么如何构造对象呢?

方法1:定义辅助构造器

def this() = this(Nil, Nil)
def this(elem: T*) = this(elem.toList, Nil)

方法2:在伴生对象中编写apply工厂方法

object Queue {
  def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)
}

(注意,scala没有全局方法,方法必须包含在类或对象中)

另一种信息隐藏的方法,直接把类本身通过暴露特质(trait)而隐藏掉

如果类或者特质声明时带类型参数,那么创建变量时也要制定具体的参数化的类型

trait Queue[T] { .. }
Queue是特质, Queue[String]是类型

泛型:通过一个能够广泛适用的类或特质,定义了许多特定的类型

在scala中,泛型类默认是非协变的子类型化

如果要表明参数的子类型化是协变的,需要变成如下形式:

trait Queue[+T] { .. }

加上-号表示需要逆变的子类型化

只要泛型的参数类型被当做方法参数的类型,那么包含它的类或特质就有可能不能与这个类型参数一起协变

class Queue[+T] {
  def append(x: T) = ...
}

下界和上界

def append[U>: T](x: U) = new Queue[U](leading, x :: tailing)

语法 U >: T 定义了T为U的下界,结果U必须是T的超类型,append的参数现在为U而不是T,返回类型也变成了 Queue[U] ,不过,自身即是自身的超类型又是自身的子类型,所以是类似小于等于的关系。

def orderedMergeSort[T <: Ordered[T]](xs: List[T]): List[T] = ...
语法 T <: Ordered[T] 定义了类型参数T具有上界 Ordered[T] ,即传递给 orderedMergeSort 的参数必须是 Ordered[T] 的子类型。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法图解

算法图解

[美] Aditya Bhargava / 袁国忠 / 人民邮电出版社 / 2017-3 / 49.00元

本书示例丰富,图文并茂,以让人容易理解的方式阐释了算法,旨在帮助程序员在日常项目中更好地发挥算法的能量。书中的前三章将帮助你打下基础,带你学习二分查找、大O表示法、两种基本的数据结构以及递归等。余下的篇幅将主要介绍应用广泛的算法,具体内容包括:面对具体问题时的解决技巧,比如,何时采用贪婪算法或动态规划;散列表的应用;图算法;K最近邻算法。一起来看看 《算法图解》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具