ImageVerifierCode 换一换
格式:DOCX , 页数:13 ,大小:29.01KB ,
资源ID:7658827      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/7658827.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Scala学习入门.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Scala学习入门.docx

1、Scala学习入门Scala入门本文源自Michel Schinz和Philipp Haller所写的A Scala Tutorial for Java programmers,由Bearice成中文,dongfengyee(东风雨)整理。1 简介本文仅在对 Scala 语言和其编译器进行简要介绍。本文的目的读者是那些已 经具有一定编程经验,而想尝试一下 Scala 语言的人们。要阅读本文,你应当具 有基础的面向对象编程的概念,尤其是 Java 语言的。2 第一个Scala例子作为学习 Scala 的第一步,我们将首先写一个标准的 HelloWorld,这个虽 然不是很有趣,但是它可以让你对

2、Scala 有一个最直观的认识而不需要太多关于 这个语言的知识。我们的 Hello world 看起来像这样:object HelloWorld def main(args: ArrayString) println(Hello, world!)程序的结构对 Java 程序员来说可能很令人怀念:它由一个 main 函数来接受命令 行参数,也就是一个 String 数组。这个函数的唯一一行代码把我 们的问候语传 递给了一个叫 println 的预定义函数。main 函数不返回值(所以它是一个 procedure method)。所以,也不需要声明返回类型。对于 Java 程序员比较陌生的是包含了

3、 main 函数的 object 语句。这样的语句定 义了一个单例对象:一个有且仅有一个实例的类。object 语 句在定义了一个叫 HelloWorld 的类的同时还定义了一个叫 HelloWorld 的实例。这个实例在第一次 使用的时候会进行实例化。聪明的读者可能会发现 main 函数并没有使用 static 修饰符,这是由于静态成员(方法或者变量)在 Scala 中并不存在。Scala 从不定义静态成员,而通过定义单例 object 取而代之。2.1 编译实例我们使用 Scala 编译器“scalac”来编译 Scala 代码。和大多数编译器一样, scalac 接受源文件名和一些选项作

4、为参数,生成一个或者多个目标文件。scala 编译生成的产物就是标准的 Java 类文件。假设我们吧上述代码保存为文件 HelloWorld.scala,我们使用下面的命令编译 它(大于号“”表示命令提示符,你不必输入它) scalac HelloWorld.scala这将会在当前目录生成一系列.class 文件。其中的一个名为 HelloWorld.class 的文件中定义了一个可以直接使用 scala 命令执行的类。下文中你可以看到这个 例子。2.2 运行实例一旦完成编译,Scala 程序就可以使用 scala 命令执行了。scala 的用法和 java 很相似,并且连选项也大致相同。上面

5、的例子就可以使用下面的命令运行, 这将会产生我们所期望的输出。 scala -classpath . HelloWorldHello, world!3 Scala与Java交互Scala 的一个强项在于可以很简单的于已有的 Java 代码交互,所有java.lang 中的类都已经被自动导入了,而其他的类需要显式声明导入。来看看演示代码吧。我们希望对日期进行格式化处理,比如说用法国的格式。 Java 类库定义了一系列很有用的类,比如 Date 和 DateFormat。由于 Scala 于Java 能够进行很好的交互,我们不需要在 Scala 类库中实现等效的代码,而只需直接吧 Java 的相关

6、类导入就可以了:import java.util.Date, Locale import java.text.DateFormat import java.text.DateFormat._ object FrenchDate def main(args: ArrayString) val now = new Dateval df = getDateInstance(LONG, Locale.FRANCE)println(df format now)Scala的import语句看上去与Java的非常相似,但是它更加强大。你可以使用大 括号来导入同一个包里的多个类,就像上面代码中第一行所做的那样

7、。另一个不 同点是当导入一个包中所有的类或者符号时,你应该使用下划线(_)而不是星 号(*)。这是由于星号在Scala中是一个有效的标识符(例如作为方法名称)。 这个例子我们稍后会遇到。第三行的 import 语句导入了 DataFormat 类中的所有成员,这使得静态方法getDateInstance 和静态变量 LONG 可以被直接引用。在 main 函数中,我们首先建立了一个 Java 的 Date 实例。这个实例默认会包含 当前时间。接下来我们一个使用刚才导入的静态函数 getDateInstance 定义了 日期格式。最后我们将使用 DataFotmat 格式化好的日期打印了出来。最

8、后一行 代码显示了 Scala 的一个有趣 的语法:只有一个参数的函数可以使用下面这样 的表达式来表示:df format now 其实就是下面的这个冗长的表达式的简洁写法 df.format(now)这看起来是一个语法细节,但是它导致一个重要的后果,我们将在下一节进行说 明。另外,我们还应当注意到 Scala 中可以直接继承或者实现 Java 中的接口和类。4 Scala:万物皆对象Scala 作为一个纯面向对象的语言,于是在 Scala 中万物皆对象,包括数字 和函数。在这方面,Scala 于 Java 存在很大不同:Java 区分原生类型(比如 boolean 和 int)和引用类型,并

9、且不能把函数当初变量操纵。4.1 数字和对象 由于数字本身就是对象,所以他们也有方法。事实上我们平时使用的算数表达式(如下例)1 + 2 * 3 / x是由方法调用组成的。它等效于下面的表达式,我们在上一节见过这个描述。 (1).+(2).*(3)./(x)这也意味着 +,-,*,/ 在Scala中也是有效的名称。在第二个表达式中的这些括号是必须的,因为 Scala 的分词器使用最长规则来进行分词。所以他会把下面的表达式:1.+(2)理解成表达项 1. ,+,和 2 的组合。这样的组合结果是由于 1.是一个有效 的表达项并且比表达项 1 要长,表达项 1.会被当作 1.0 ,使得它成为一个 d

10、ouble 而不是 int。而下面的表达式阻止了分析器错误的理解(1).+(2)4.2 函数与对象函数在 Scala 语言里面也是一个对象,也许这对于 Java 程序员来说这比较 令人惊讶。于是吧函数作为参数进行传递、把它们存贮在变量中、或者当作另一 个函数的返回值都是可能的。吧函数当成值进行操作是函数型编程语言的基石。为了解释为什么吧函数当作值进行操作是十分有用的,我们来考虑一个计时 器函数。这个函数的目的是每隔一段时间就执行某些操作。那么如何吧我们要做 的 操作传入计时器呢?于是我们想吧他当作一个函数。这种目前的函数对于经 常进行用户界面编程的程序员来说是最熟悉的:注册一个回调函数以便在事

11、件发 生后得到 通知。在下面的程序中,计时器函数被叫做 oncePerSceond,它接受一个回调函数 作为参数。这种函数的类型被写作 () = Unit ,他们不接受任何参数也没有任 何返回(Unit 关键字类似于 C/C+中的 void)。程序的主函数调用计时器并传 递一个打印某个句子的函数作为回调。换 句话说,这个程序永无止境的每秒打 印一个“time flies like an arrow”。object Timer def oncePerSecond(callback: () = Unit) while (true) callback(); Thread sleep 1000 def

12、 timeFlies() println(time flies like an arrow.)def main(args: ArrayString) oncePerSecond(timeFlies)注意,我们输出字符串时使用了一个预定义的函数 println 而不是使用 System.out 中的那个。4.2.1 匿名函数我们可以吧这个程序改的更加易于理解。首先我们发现定义函数 timeFlies 的唯一目的就是当作传给 oncePerSecond 的参数。这么看来 给这种只用一次的 函数命名似乎没有什么太大的必要,事实上我们可以在用到这个函数的时候再定 义它。这些可以通过匿名函数在 Scal

13、a 中实现,匿名函数顾名 思义就是没有名 字的函数。我们在新版的程序中将会使用一个匿名函数来代替原来的 timeFlise 函数,程序看起来像这样:object TimerAnonymous def oncePerSecond(callback: () = Unit) while (true) callback(); Thread sleep 1000 def main(args: ArrayString) oncePerSecond() =println(time flies like an arrow.)本例中的匿名函数使用了一个箭头(=)吧他的参数列表和代码分开。在这 里参数列表是空的,

14、所以我们在右箭头的左边写上了一对空括号。函数体内容与 上面的 timeFlise 是相同的。5 Scala类正如我们所见,Scala 是一门面向对象的语言,因此它拥有很多关于“类” 的描述 。Scala 类使用和 Java 类似的语法进行定义。但是一个重要的不同点在 于 Scala 中的类可以拥有参数,这样就可以得出我们下面关于对复数类(Complex)的定义:class Complex(real: Double, imaginary: Double) def re() = realdef im() = imaginary我们的复数类(Complex)接受两个参数:实部和虚部。这些参数必须在实

15、例化 时进行传递,就像这样:new Complex(1.5, 2.3)。类定义中包括两个叫做 re和 im 的方法,分别接受上面提到的两个参数。值得注意的是这两个方法的返回类型并没有显式的声明出来。他们会被编译 器自动识别。在本例中他们被识别为 Double 但是编译器并不总是像本例中的那 样进行自动识别。不幸的是关于什么时候识别,什么时候不识别的规则相当冗杂。 在实践中这通常不会成为一个问题,因为 当编译器处理不了的时候会发出相当的抱怨。作为一个推荐的原则,Scala 的新手们通常可以试着省略类型定义而让 编译器通过上下文自己判断。久而久之,新 手们就可以感知到什么时候应该省 略类型,什么时

16、候不应该。5.1 无参方法关于方法 re 和 im 还有一个小问题:你必须在名字后面加上一对括号来调用 它们。请看下面的例子:object ComplexNumbers def main(args: ArrayString) val c = new Complex(1.2, 3.4)println(imaginary part: + c.im()你可能觉得吧这些函数当作变量使用,而不是当作函数进行调用,可能会更加令 人感到舒服。事实上我们可以通过定义无参函数在 Scala 做到这点。这类 函数 与其他的具有 0 个参数的函数的不同点在于他们定义时不需要在名字后面加括 弧,所以在使用时也不用加(

17、但是无疑的,他们是函数),因此,我们的 Complex 类可以重新写成下面的样子;class Complex(real: Double, imaginary: Double) def re = realdef im = imaginary5.2 继承和覆盖Scala 中的所有类都继承一个父类,当没有显示声明父类时(就像上面定义 的 Complex 一样),它们的父类隐形指定为 scala.AnyRef。在子类中覆盖父类的成员是可能的。但是你需要通过 override 修饰符显示指定 成员的覆盖。这样的规则可以避免意外覆盖的情况发生。作为演示,我们在 Complex 的定义中覆盖了 Object

18、 的 toString 方法。class Complex(real: Double, imaginary: Double) def re = realdef im = imaginaryoverride def toString() = + re + (if (im 5 这个定义了一个函数:当参数等于字符串x 时返回整数 5,否则抛出异常。 在编写求值函数之前我们,我们需要给我们的上下文起个名字,以便在后面的代码里面引用。理所应当的我们使用了类型 String=Int,但 是如果我们给这个类型起个名字,将会让程序更加简单易读,而且更加容易维护。在 scala 中,这件事情可以通过以下代码完成:

19、type Environment = String = Int从现在开始,类型 Environment 就当作 String 到 Int 的函数类型名来使用了。 现在我们可以开始定义求值函数了。从概念上来说,这是很简单的一个过程:两个表达式之和等于两个表达式分别求值后再求和;变量的值可以从上下文中提取;常量的值就是他本身。在 Scala 中表达这个没有什么难度:def eval(t: Tree, env: Environment): Int = t match case Sum(l, r) = eval(l, env) + eval(r, env) case Var(n) = env(n)ca

20、se Const(v) = v求值函数通过对树 t 进行模式匹配来完成工作。直观的来看,上述代码的思路是 十分清晰的:1. 第一个模式检查传入的树的根节点是否是一个 Sum,如果是,它将会吧树 的左边子树赋值给 l,右边的子树赋值给 r,然后按照箭头后面的代码进行 处理;这里的代码可以(并且的确)使用了在左边匹配时所绑定的变量, 比如这里的 l 和 r。2. 如果第一个检查没有成功,表明传入的树不是 Sum,程序继续检查他是不 是一个 Var;如果是,则吧变量名赋给 n 然后继续右边的操作。3. 如果第二个检查也失败了,表示 t 既不是 Sum 也不是 Var,程序检查他是 不是 Const。

21、如果是着赋值变量并且继续。4. 最后,如果所有检查都失败了。就抛出一个异常表示模式匹配失败。这只有在 Tree 的其他之类被定义时才可能发生。我们可以看出模式匹配的基本思想就是试图对一个值进行多种模式的匹配,并且 在匹配的同时将匹配值拆分成若干子项,最后对匹配值与其子项执行某些代码。一个熟练的面向对象的程序员可能想知道为什么我们不吧 eval 定义为 Tree 或者 其之类的成员函数。我们事实上可以这么做。因为 Scala 允许条件类象普通类那 样定义成员。决定是否使用模式匹配或者成员函数取决于程序员的喜好,不过这 个取舍还和可扩展性有重要联系:1. 当你使用成员函数时,你可以通过继承 Tre

22、e 从而很容易的添加新的节点 类型,但是另外一方面,添加新的操作也是很繁杂的工作,因为你不得不 修改 Tree 的所有子类。2. 当你使用模式匹配是,形势正好逆转过来,添加新的节点类型要求你修改 所有的对树使用模式匹配的函数,但是另一方面,添加一个新的操作只需要再添加一个模式匹配函数就可以了。下面我们来更详细的了解模式匹配,让我们再给表达式定义一个操作:对符号求 导数。读者们也许想先记住下面关于此操作的若干规则:1. 和的导数等于导数的和,2. 如果符号等以求导的符号,则导数为 1,否则为 0.3. 参数的导数永远为 0。上述规则可以直接翻译成 Scala 代码:def derive(t: T

23、ree, v: String): Tree = t match case Sum(l, r) = Sum(derive(l, v), derive(r, v)case Var(n) if (v = n) = Const(1)case _ = Const(0)这个函数使用了两个关于模式匹配的功能,首先 case 语句可以拥有一个 guard 子句:一个 if 条件表达式。除非 guard 的条件成立,否则该模式不会成功匹配。 其次是通配符:_ 。这个模式表示和所有值匹配而不对任何变量赋值。事实上我们还远没有触及模式匹配的全部精髓。但是我们限于篇幅原因不得不再 此停笔了。下面我们看看这个两个函数是

24、如何在一个实例上运行的。为了达到这 个目前我们写了一个简单的 main 函数来对表达式(x + x ) + (7 + y )进行若干 操作:首先计算当x 5, y 7时表达式的值,然后分别对 x 和 y 求导。def main(args: ArrayString) val exp: Tree = Sum(Sum(Var(x),Var(x),Sum(Const(7),Var(y)val env: Environment = case x = 5 case y = 7 println(Expression: + exp)println(Evaluation with x=5, y=7: + eva

25、l(exp, env)println(Derivative relative to x:n + derive(exp, x)println(Derivative relative to y:n + derive(exp, y)执行程序,我们能得到以下输出:Expression: Sum(Sum(Var(x),Var(x),Sum(Const(7),Var(y) Evaluation with x=5, y=7: 24Derivative relative to x:Sum(Sum(Const(1),Const(1),Sum(Const(0),Const(0) Derivative relati

26、ve to y: Sum(Sum(Const(0),Const(0),Sum(Const(0),Const(1)通过研究程序输出,我们能看到求导的输出可以在被打印之前简化,使用模式匹 配定义一个简化函数是挺有意思的(不过也需要一定的技巧)工作。读者可以尝 试自己完成这个函数。7 Scala Trait除了从父类集成代码外,Scala 中的类还允许从一个或者多个 traits 中导 入代码。对于 Java 程序员来说理解 traits 的最好方法就是把他们当作可以包含代码的接 口(interface)。在 Scala 中,当一个类继承一个 trait 时,它就实现了这个 trait 的接口,同时

27、还从这个 trait 中继承了所有的代码。让我们通过一个典型的实例来看看这种 trait 机制是如何发挥作用的:排序对 象。能够比较若干给定类型的对象在实际应用中是很有用的,比如在进行排 序 时。在 Java 语言中可以比较的对象是通过实现 Comparable 接口完成的 。在 Scala 中我们可以通过吧 Comparable 定义为 trait 来做的比 Java 好一些。我们 吧这个 trait 叫做 Ord。在比较对象时,一下六种关系通常使用率最高:小于、小于等于、等于、不等于、 大于等于、大于。但是把他们都定义一次无疑是很没用而且繁琐的。尤其是 六 种关系中的四种其实是可以通过其他两种关系导出的。例如给

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1