内容简介:Scala被有人戏称是 “太阳系最难的语言” ,那我们来看看他那些各种奇怪的符号使用吧,不少用法只能用 “惊为天书” 来形容啊。说明:本文为学习笔记,下面内容多数来源于网上多篇文档的汇总,在此感谢原作者们。scala中泛型使用
Scala被有人戏称是 “太阳系最难的语言” ,那我们来看看他那些各种奇怪的符号使用吧,不少用法只能用 “惊为天书” 来形容啊。
说明:本文为学习笔记,下面内容多数来源于网上多篇文档的汇总,在此感谢原作者们。
泛型
:
scala中泛型使用 []
指定泛型的类型参数,上下文界定是隐式参数的语法糖
-
:表示上下文界定,如A:B表示 B 可以进行隐式转化的A类型
示例:
-
T:A:B表示即同时满足AT这种隐式值和BT这种隐式值
<:
与 :>
-
<:表示只限定子类,如T <: A表示T必须为A的子类 -
>:表示只限定子类,如T >: A表示T必须为A的父类
<:
与 :>
相当于 java 范型编程中的extends,super对泛型变量的限定。
示例:
-
T <: A with B表示A和B为T上界 -
T >: A with B表示A和B为T下界 -
T >: A <: B表示同时拥有上界和下界,并且A为下界,B为上界,A为B的子类,顺序不能颠倒。
<%
<%
表示“view bounds”(视界),比 <:
适用的范围更广,除了所有的子类型,还允许隐式转换过去的类型。
示例:
-
T <% A <% B表示:同时能够满足隐式转换的A和隐式转换的B
=:=
, <:<
与 <%<
这些被称为广义的类型约束。他们允许你从一个类型参数化的class或trait,进一步约束其类型参数之一。
-
=:=表示必须是这种类型,如A =:= B表示 A 必须是 B 类型 -
<:<表示必须是子类型,如A <:< B表示 A 必须是B的子类型 (类似于简单类型约束<:) -
<%<表示必须是可视化类型,A <%< B表示 A 必须是可视化为 B类型, 可能通过隐式转换 (类似与简单类型约束<%)
[+T]
与 [-T]
在泛型中,需要描述泛型的类型之间 继承
关系可以转化的关系。
[+T] [-T]
[+T]
A是B的子类,如果想让Container[A]是Container[B]的子类,那么只需在定义Container的时候加上“[+T]”就好了;但是注意,如果只加了“[+T]”,只可以实例化从父类到子类的引用。
假定,Bird extends Animal extends Earth
当有 Space[+T]
定义时,
var a = new Space[Animal]
a = new Space[Bird]
但不能是
var a = new Space[Animal]
a = new Space[Earth]
[-T]
面向对象编程中,子类也可以指向父类的引用,在正常情况下,通过强制类型转换,是可以实现。但是在有集合类的情况下如何实现呢?这就是“协变” [-T]
了,只需在定义集合类的时候在集合类上加上“[-T]”即可。
假定,Bird extends Animal extends Earth
当有 Space[-T]
类定义时,
则如下是OK的:
var a = new Space[Animal]
a = new Space[Earth]
列表操作符
::
与 ++
::
表示普通元素与List的连接操作,示例:
val a = 1
val b = List(3, 4)
val c = 1 :: b
++
表示用于连接两个集合, 示例
val a = List(1, 2)
val b = List(3, 4)
val c = a ++ b
其中a,b保持不变,a和b连接产生一个新表List(1,2,3,4),而不是在a上面做add操作。
++=
表示连接两个集合,并赋值,示例
var a = List(1, 2)
a ++= List(3, 4)
:::
:::
表示只能List的连接操作,示例:
val a = List(1, 2)
val b = List(3, 4)
val c = a ::: b
其中a,b保持不变,a和b连接产生一个新表List(1,2,3,4),而不是在a上面做add操作。
:+
与 +:
:+ +:
示例:
"A"+:"B"+:Nil Nil:+"A":+"B"
则c的结果是List(1,3,4),需要注意的是, 1 :: b
操作, ::
是右侧对象的方法,即它是b对象的方法,而 ::
左侧的运算数是 ::
方法的参数,所以 1::b
的含义是 b.::(1)
成对的符号
->
与 <-
-
->是所有Scala对象都有的方法,生成元组,如A->B结果是返回一个二元的元组(A,B) -
<-用于for循环中,<-在Scala中称为generator,在每次遍历的过程中,生成一个新的对象A,这个A是val,而不是var
<=
与 =>
<= =>
=>
用法
Call by name
传名调用
(Call by name) ,在
传名调用` 求值中,根本就不求值给函数的实际参数,而是使用避免捕获代换把函数的实际参数直接代换入函数体内。如果实际参数在函数的求值中未被用到,则它永不被求值;如果这个实际参数使用多次,则它每次都被重新求值。
传名调用求值超过传值调用求值的优点是传名调用求值在一个值存在的时候总是生成这个值,而传名调用可能不终止如果这个函数的实际参数是求值这个函数所不需要的不终止计算。反过来说,在函数的实际参数会用到的时候传名调用就非常慢了,这是因为实践中几乎总是要使用如 thunk 这样的机制。
传需求调用(Call by need)
, 传需求调用
是 传名调用
的记忆化版本,如果 “函数的实际参数被求值了”,这个值被存储起来已备后续使用。在“纯”(无副作用)设置下,这产生同传名调用一样的结果;当函数实际参数被使用两次或更多次的时候,传需求调用总是更快。
示例:
object TargetTest2 extends Application {
def loop(body: => Unit): LoopUnlessCond =
new LoopUnlessCond(body)
protected class LoopUnlessCond(body: => Unit) {
def unless(cond: => Boolean) {
body
if (!cond) unless(cond)
}
}
var i = 10
loop {
println("i = " + i)
i -= 1
} unless (i == 0)
}
上面的程序运行结果是
i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1
函数定义
使用方式一:In a value:it introduces a function literal(通译为匿名函数,有时候也叫函数显式声明,函数字面量), or lambda(参考lambda表达式的文章,其实也是匿名函数),示例:
List(1,2,3).map { (x: Int) => x * 2 }
使用方式二:in a type, with symbols on both sides of the arrow (e.g. A => T, (A,B) => T, (A,B,C) => T, etc.) it’s sugar(syntactic sugar语法糖) for Function
示例:
scala> val f: Function1[Int,String] = myInt => "my int: "+myInt.toString
f: (Int) => String = <function1>
scala> f(0)
res0: String = my int: 0
scala> val f2: Int => String = myInt => "my int v2: "+myInt.toString
f2: (Int) => String = <function1>
scala> f2(1)
res1: String = my int v2: 1
scala> val f2: Function2[Int,Int,String] = (myInt1,myInt2) => "This is my function to transfer " + myInt1 + " and " + myInt2 + " as a string component."
f2: (Int, Int) => String = <function2>
scala> f2(1,2)
res6: String = This is my function to transfer 1 and 2 as a string component.
scala> val f22:(Int,Int)=>String = (myInt1,myInt2) => "This is my function to transfer " + myInt1 + " and " + myInt2 + " as a string component."
f22: (Int, Int) => String = <function2>
scala> f22(2,4)
res7: String = This is my function to transfer 2 and 4 as a string component.
Here myInt is binded to the argument value passed to f and f2.
() => T is the type of a function that takes no arguments and returns a T. It is equivalent to Function0[T]. () is called a zero parameter list I believe.
scala> val f: () => Unit = () => { println("x")}
f: () => Unit = <function0>
scala> f()
x
使用方式三:Empty parens on the left hand side (e.g. () => T) indicate that the function takes no parameters (also sometimes called a “thunk”);
示例:
object TimerAnonymous {
def oncePerSecond(callback: () => Unit) {
while (true) { callback(); Thread sleep 1000 }
}
def main(args: Array[String]) {
oncePerSecond(() => println("time flies like an arrow..."))
}
}
模式匹配
这个比较容易理解,示例:
object MatchTest extends App {
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
println(matchTest(3))
}
自身类型(self type)
When a trait extends a class, there is a guarantee that the superclass is present in any class mixing in the trait. Scala has analternate mechanism for guaranteeing this: self types. When a trait starts out with this: Type => then it can only be mixed into a subclass of the given type.
示例
scala> trait LoggedException {
| this: Exception =>
| def log(): Unit = {
| println("Please check errors.")
| }
| }
defined trait LoggedException
scala> import java.io.File
import java.io.File
scala> val file = new File("/user") with LoggedException
<console>:13: error: illegal inheritance;
self-type java.io.File with LoggedException does not conform to LoggedException's selftype LoggedException with Exception
val file = new File("/user") with LoggedException
在定义LoggedException使用了this: Exception =>那么意味着LoggedException只能被“混入”Exception的子类中,因为File不是Exception的子类,所以报错。
下划线 _
作为标识符
例如定义一个变量 val _num = 123
作为通配符
import语句
例如 import scala.math._
case语句
object MatchTest extends App {
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
println(matchTest(3))
}
元组(tuple)访问
scala> val t = (1, 3.14, "Fred")
t: (Int, Double, String) = (1,3.14,Fred)
//可以用_1,_2,_3访问这个元组
scala> t._1
res3: Int = 1
scala> t._2
res4: Double = 3.14
scala> t._3
res5: String = Fred
可以通过模式匹配获取元组的元素,当不需要某个值的时候可以使用_替代,例如:
scala> val t = (1, 3.14, "Fred")
t: (Int, Double, String) = (1,3.14,Fred)
scala> val (first, second, _) = t
first: Int = 1
second: Double = 3.14
scala> val (first, _, _) = t
first: Int = 1
将方法转换为函数
请参见 Scala中Method方法和Function函数的区别
作为函数的参数
一个匿名的函数传递给一个方法或者函数的时候,scala会尽量推断出参数类型。例如一个完整的匿名函数作为参数可以写为:
scala> def compute(f: (Double)=>Double) = f(3)
compute: (f: Double => Double)Double
//传递一个匿名函数作为compute的参数
scala> compute((x: Double) => 2 * x)
res1: Double = 6.0
如果参数x在=>右侧只出现一次,可以用_替代这个参数,简写为:
scala> compute(2 * _)
res2: Double = 6.0
更常见的使用方式为:
scala> (1 to 9).filter(_ % 2 == 0)
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8)
scala> (1 to 3).map(_ * 3)
res1: scala.collection.immutable.IndexedSeq[Int] = Vector(3, 6, 9)
以上所说的为一元函数,那么对于二元函数,即有两个参数x和y的函数,是如何使用 _
的?下面方法需要的参数是一个二元函数,而且函数参数的类型为 T
,可以用 _
分别表示二元函数中的参数 x
和 y
。例如:
scala> List(10, 5, 8, 1, 7).sortWith(_ < _)
res0: List[Int] = List(1, 5, 7, 8, 10)
下划线和其他符号组合的使用方式
下划线与等号 _=
自定义setter方法,请参见 Overriding def with var in Scala
下划线与星号 _*
变长参数
例如定义一个变长参数的方法sum,然后计算 1-5
的和,可以写为:
scala> def sum(args: Int*) = {
| var result = 0
| for (arg <- args) result += arg
| result
| }
sum: (args: Int*)Int
scala> val s = sum(1,2,3,4,5)
s: Int = 15
但是如果使用这种方式就会报错
scala> val s = sum(1 to 5)
<console>:12: error: type mismatch;
found : scala.collection.immutable.Range.Inclusive
required: Int
val s = sum(1 to 5)
^
这种情况必须在后面写上: _*
将 1 to 5
转化为参数序列
scala> val s = sum(1 to 5: _*)
s: Int = 15
变量声明中的模式
例如,下面代码分别将arr中的第一个和第二个值赋给first和second
scala> val arr = Array(1, 2, 3, 4, 5)
arr: Array[Int] = Array(1, 2, 3, 4, 5)
scala> val Array(1, 2, _*) = arr
scala> val Array(first, second, _*) = arr
first: Int = 1
second: Int = 2
参考:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- macos – dyld:惰性符号绑定失败:未找到符号:_PQsetErrorContextVisibility
- 嵌入式C语言自我修养 09:链接过程中的强符号和弱符号
- “Bug-O” 符号
- 简单理解符号执行技术
- GCC 符号表小结
- Golang 格式输出符号
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构与算法分析
Mark Allen Weiss / 冯舜玺 / 电子工业出版社 / 2016-8 / 89.00元
本书是数据结构和算法分析的经典教材,书中使用主流的程序设计语言C++作为具体的实现语言。书中内容包括表、栈、队列、树、散列表、优先队列、排序、不相交集算法、图论算法、算法分析、算法设计、摊还分析、查找树算法、k-d树和配对堆等。本书把算法分析与C++程序的开发有机地结合起来,深入分析每种算法,内容全面、缜密严格,并细致讲解精心构造程序的方法。一起来看看 《数据结构与算法分析》 这本书的介绍吧!