中国领先的IT技术网站
|
|

初探Scala编程:解释器,变量及函数定义

本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第二章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

作者:Martin Odersky等来源:Artima|2009-07-08 16:52


在我们开始深度Scala教程之前,我们将用两章来给你画一张Scala大致的图纸,更重要的是,带你写一些代码。我们鼓励你实际尝试所有出现在本章以及后续章节中的代码例子。开始学习Scala最好的方法就是用它编程。

51CTO编辑推荐:Scala编程语言专题

要执行本章的例子,你应该有一份标准的Scala安装。想要的话,可以到http://www.scala-lang.org/downloads并依照你的平台的向导。你也可以使用Eclipse,IntelliJ,或NetBeans的Scala插件,但是对于本章的这几步来说,我们假设你用的是从scala-lang.org拿到的Scala发布包。

第一步:学习使用Scala解释器

开始Scala最简单的方法是使用Scala解释器,它是一个编写Scala表达式和程序的交互式“shell”。简单地在解释器里输入一个表达式,它将计算这个表达式并打印结果值。Scala的交互式shell就叫做scala。你可以在命令提示符里输入scala使用它:

  1. $ scala  
  2. Welcome to Scala version 2.7.2.  
  3. Type in expressions to have them evaluated.  
  4. Type :help for more information.  
  5. scala> 

在你输入表达式,如1 + 2,并敲了回车之后:

  1. scala> 1 + 2 

解释器会打印:

  1. res0: Int = 3 

这行包括:

一个自动产生的或用户定义的名称说明计算的值(res0,表示结果0),

一个冒号(:),跟着表达式的类型(Int),

一个等号(=),

计算表达式所得到的结果(3)。

Int类型指代了scala包的类Int。Scala里的包与Java里的包很相似:它们把全局命名空间分区并提供了信息隐藏的机制。 类Int的值对应着Java的int值。更广泛意义上来说,所有的Java原始类型在scala包里都有对应的类。例如,scala.Boolean对应着Java的boolean。scala.Float对应着Java的float。当你把你的Scala代码编译成Java字节码,Scala编译器将使用Java的原始类型以便获得其带来的性能益处。

resX识别符还将用在后续的代码行中。

例如,既然res0已在之前设为3,res0 * 3就是9:

  1. scala> res0 * 3  
  2. res1: Int = 9 

打印必要的,却不仅此而已的,Hello, world! 贺词,输入:

  1. scala> println("Hello, world!")  
  2. Hello, world! 

println函数在标准输出上打印传给它的字串,就跟Java里的System.out.println一样。

第二步:定义一些变量

Scala有两种变量,val和var。val类似于Java里的final变量。一旦初始化了,val就不能再赋值了。与之对应的,var如同Java里面的非final变量。var可以在它生命周期中被多次赋值。下面是一个val的定义:

  1. scala> val msg = "Hello, world!" 
  2. msg: java.lang.String = Hello, world!  

这个语句引入了msg当作字串"Hello, world!"的名字。类型是java.lang.String,因为Scala的字串是由Java的String类实现的。

如果你之前曾定义过Java变量,你会发现一个很醒目的差别:无论java.lang.String还是String都没有出现在val的定义中。本例演示了类型推断:type inference,这种Scala能自动理解你省略的类型的能力。在这个例子里,因为你用一个字串文本初始化了msg,Scala推断msg的类型是String。如果Scala解释器(或编译器)可以推断类型,那么让它这么做而不是写一些没必要的显式类型标注常常是最好的选择。不过,如果你愿意,也可以显式地定义类型,也许有些时候你也应该这么做。显式的类型标注不但可以确保Scala编译器推断你倾向的类型,还可以作为将来代码读者有用的文档。Java中变量的类型指定在其名称之前,与之不同的是,Scala里变量的类型在其名称之后,用冒号分隔。如:

  1. scala> val msg2: java.lang.String = "Hello again, world!" 
  2. msg2: java.lang.String = Hello again, world!  

或者,因为在Scala程序里java.lang类型的简化名 也是可见的,所以可以简化为:

  1. scala> val msg3: String = "Hello yet again, world!" 
  2. msg3: String = Hello yet again, world! 

回到原来的那个msg,现在它定义好了,你可以按你的想法使用它,如:

  1. scala> println(msg)  
  2. Hello, world!  

你对msg不能做的,因为是val而不是var,就是再给它赋值。 例如,看看你做如下尝试的时候编译器怎么报错的:

  1. scala> msg = "Goodbye cruel world!" 
  2. <console>:5: error: reassignment to val  
  3.          msg = "Goodbye cruel world!" 
  4.               ˆ  

如果可重赋值是你需要的,你应使用var,如下:

  1. scala> var greeting = "Hello, world!" 
  2. greeting: java.lang.String = Hello, world!  

由于greeting是var而不是val,你可以在之后对它重新赋值。比如说,如果你之后心态不平了,你可以修改你的greeting为:

  1. scala> greeting = "Leave me alone, world!" 
  2. greeting: java.lang.String = Leave me alone, world! 

要输入一些能跨越多行的东西,只要一行行输进去就行。如果输到行尾还没结束,解释器将在下一行回应一个竖线。

  1. scala> val multiLine =  
  2.       | "This is the next line."  
  3. multiLine: java.lang.String = This is the next line.  

如果你意识到你输入了一些错误的东西,而解释器仍在等着你更多的输入,你可以通过按两次回车取消掉:

  1. scala> val oops =  
  2.       |  
  3.       |  
  4. You typed two blank lines. Starting a new command.  
  5. scala> 

本书后续部分,我们将省略竖线以便让代码更易于阅读(并易于从PDF电子书中复制粘贴到解释器里)。

第三步:定义一些函数

现在你已经用过了Scala的变量,或许想写点儿函数。下面是在Scala里面的做法:

  1. scala> def max(x: Int, y: Int): Int = {  
  2.      if (x > y) x  
  3.      else y  
  4.     }  
  5. max: (Int,Int)Int  

函数的定义用def开始。函数名,本例中是max,跟着是括号里带有冒号分隔的参数列表。每个函数参数后面必须带前缀冒号的类型标注,因为Scala编译器(还有解释器,但之后我们将只说编译器)没办法推断函数参数类型。本例中,名叫max的函数带有两个参数,x和y,都是Int类型。在max参数列表的括号之后你会看到另一个“: Int”类型标注。这个东西定义了max函数的结果类型:result type。 跟在函数结果类型之后的是一个等号和一对包含了函数体的大括号。本例中,函数体里仅有一个if表达式,选择x或者y,哪个较大,就当作max函数的结果。就像这里演示的,Scala的if表达式可以像Java的三元操作符那样产生一个值。举例来说,Scala表达式“if (x > y) x else y”与Java里的“(x > y) ? x : y”表现得很像。在函数体前的等号提示我们函数式编程的世界观里,函数定义一个能产生值的表达式。函数的基本结构在图2.1里面演示。

Scala函数的基本构成

有时候Scala编译器会需要你定义函数的结果类型。比方说,如果函数是递归的, 你就必须显式地定义函数结果类型。然而在max的例子里,你可以不用写结果类型,编译器也能够推断它。 同样,如果函数仅由一个句子组成,你可以可选地不写大括号。这样,你就可以把max函数写成这样:

  1. scala> def max2(x: Int, y: Int) = if (x > y) x else y  
  2. max2: (Int,Int)Int  

一旦你定义了函数,你就可以用它的名字调用它,如:

  1. scala> max(3, 5)  
  2. res6: Int = 5 

还有既不带参数也不返回有用结果的函数定义:

  1. scala> def greet() = println("Hello, world!")  
  2. greet: ()Unit  

当你定义了greet()函数,解释器会回应一个greet: ()Unit。“greet”当然是函数名。空白的括号说明函数不带参数。Unit是greet的结果类型。Unit的结果类型指的是函数没有返回有用的值。Scala的Unit类型比较接近Java的void类型,而且实际上Java里每一个返回void的方法都被映射为Scala里返回Unit的方法。因此结果类型为Unit的方法,仅仅是为了它们的副作用而运行。在greet()的例子里,副作用是在标准输出上打印一句客气的助词。

下一步,你将把Scala代码放在一个文件中并作为脚本执行它。如果你想离开解释器,输入:quit或者:q。

  1. scala> :quit  

本文节选自《Programming in Scala》 

【相关阅读】

  1. 影响Scala语言设计的因素列表
  2. 喜欢Scala编程的四个理由
  3. Scala融合面向对象和函数概念的方法
  4. Scala的语言特性——可伸展的语言
  5. 学习Scala中的Case类
【责任编辑:Reno TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

XML案例解析教程

本书对开发XML应用程序给予了详细指导,其中一些应用程序甚至对于您来说是完全陌生的。这些应用程序包括XML 1.0,以及与XSLT、XQuery和XPat...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
× 学习达标赢Beats耳机