Scala基础入门-1

首先需要Scala开发环境的搭建,网上自己找教程。

声明常量与变量

val foo = 0  // 常量
var bar = 0  // 变量

  

在Scala中,更加鼓励使用val来进行声明,也就是推荐使用常量。在大部分时候不需要分号,看自己便好…虽然是强静态类型的语言,但是却并没有写出类型——这就是类型推断。

也可以指定类型:

val spark: String = "Hello Scala"

  

多个的同时声明:

val xInt, yInt = 100  // xInt, yInt都声明为100

  

常用类型

与Java类似,Scala中也有表示数值类型的类,但是Scala不区分基本类型和引用类型。也就是说Scala中一切都是对象了吧。

Scala对Java中的数值类型的功能添加了许多功能上的强化,这其中涉及了隐式转换。比如,String类型转换成StringOps类型。还有RichInt、RichDouble等对应Int、Double类型。至于有什么新加的方法,可以查看Scala API。

操作符

算数操作符应该与预期中的没有什么差异,因为如果有差异用起来就有麻烦了。但算数操作符事实上是方法。

a + b  相当于  a.+(b)

对于带有一个隐式参数和一个显式参数的方法来说,通常都可以这么简化。如:

1.to(10)  这会产生一个1-10的Range实例,相当于  1 to 10

两种风格任君选择。

Scala没有提供++和–(别认为我打错了,这里是有两个-号的)这两个其他语言中很常见常用的操作符,理由是:不值得为少按一个键而额外增加一个特例。

Scala中可以对操作符进行重载。

调用函数和方法

函数(function)和方法(method)

 

引入相应的包后,使用函数很简单,不需要从某个类中调用静态方法。

math.sqrt(2)  等同于  scala.math.sqrt(2)  // 以scala.开头的包名,都可以省去该前缀

  

Scala中没有静态方法,但是有单例对象和伴生对象之类的概念可以实现差不多的特性,这些估计到后面会细说。

BigInt.probablePrime(100, util.Random)

  

通常,使用没有参数,且不改变当前对象的方法时,不带圆括号。

apply方法

s是一个字符串,而 s(4) 将会返回该字符串中第4个字符。在使用形如 s(4) 这种,类似于函数调用的语法时,实际上是调用了 apply() 方法。也就是说, s(4) 等于 s.apply(4)

 

两个容易混淆的概念——表达式(expression)和语句(statement)。表达式有值,而语句没有值只执行操作。

要说起这一点的原因是,Scala中,几乎所有构造出来的语法结构都有值,也就是表达式。这可以使程序更加精简易读。

举例,if表达式是有值的;语句块也是有值的,是最后一个表达式的值。

而对应Java中的void,Scala中有一个类型Unit,用来表示“无有用值”(void表示没有值,这里还是有差别的)。

条件表达式

if/else表达式在Scala中是有值的,除开这一点,与Java等语言没有什么差别。而有值所带来的效果,可以是下面这样子的:

val s = if (x > 0) 1 else -1
// 与上等效的另一种写法
if (x > 0) s = 1 else s = -1

  

第一种写法可以用来初始化一个val,而第二种则要求s必须是var。前面也说了,Scala中更鼓励使用val。

同时,这样的条件表达式,还可以实现比三目操作符 ? : 更加灵活的功能。

如果在if和else两边的类型是不同的,比如:

if (x > 0) "positive" else -1

  

这时返回的类型将会是两个分支类型的公共超类型(这里是String和Int的公共超类型Any)。

如果没有else部分,那么if表达式有可能不返回值,这时候就需要前面提到的Unit类型,写做()。

if (x > 0) 1
// 这相当于else部分返回Unit类型,相当于下面的写法
if (x > 0) 1 else ()

  

 

语句终止

分号不是必须的,但是使用它们不会带来坏处。

能根据上下文判断出语句结尾的地方,都不需要使用分号。怎么判断是否语句结尾,以后在编码过程中会慢慢积累起感觉的。

如果需要在单行中写多个语句,那么就需要使用分号了。而在写一个较长的语句时,如果需要分行写,要确保前面的行尾需要以不能用来语句结尾的符号来结束。比如说像是下面的操作符:

 

a = s0 + (v - v0) * t + // +说明了这里不是语句结束哦
  0.5 + (a - a0) * t * t
// 下面这种写法就会出问题了
a = s0 + (v - v0) * t
  + 0.5 + (a - a0) * t * t

  

这里还可以发现的是,Scala推荐使用两个空格来缩进。

由于判断语句终止的需要,Scala程序员更倾向于使用Kernighan & Ritchie风格的花括号(也就是Java中推荐的方法):

 

if (n > 0) {
  r = r * n
  n -= 1
}

  

块表达式和赋值

Scala中,{ }包含一系列的表达式,而且{ }最后返回的也是一个表达式——最后一个表达式。

而赋值语句的值,是Unit类型的。这也大体上决定了在Java和C++中的 x = y = 1 会出现的赋值语句,几乎不可能出现在Scala中。

 

输入和输出

输入函数: readLine()、 readInt()等,用来从控制台Console中读取输入。

而输出部分,则是很常见的 print()、 println()、 printf()。

循环

看看Scala中的for循环:

for (i <- expression)  // 让i遍历表达式的所有值,遍历的执行取决于表达式的类型
// 例子
for (i <- 1 to n)  // 遍历1到n
for (i <- 0 util s.length)  // 遍历0到s.length-1
// 遍历求和
var sum = 0
for (ch <- "Hello") sum += ch

  

事实上,Scala中更多是使用函数式而不是指令式的风格来遍历等操作。后面会见到的。

高级for循环和for推导式

在一个for循环中使用多个“generator”(生成器):

 

for (i <- 1 to 3; j <- 1 to 3) print ((10 * i +j) + " "
// 结果:11 12 13 21 22 23 31 32 33

  

在for循环中使用“filter ”(过滤器,守卫)来过滤一部分不符合条件的枚举情况:

for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10 * i + j) + " ")
// 结果:12 13 21 23 31 32

  

可以使用多个filter,中间需要使用分号分隔。

可以在for表达式中使用任意多的定义,引入在循环中使用变量:

for (i <- 1 to 3; from = 4 - i; j <- from to 3) print ((10 * i + j) + " ")
// 结果:13 22 23 31 32 33

  

如果for表达式的循环体是以yield关键词开始的,那么该循环会构造出一个集合,这被称为for推导式。推导式生成的集合与第一个generator是类型兼容的。

for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar
// 生成"HIeflmlmop"

  

还可以将for表达式的小括号换成花括号,这样可以省去写分号的功夫。

for表达式内容不少,而且这里也不是全部。在《Programming in Scala》中,还有一些高级的讨论,这里就先略了,先入个门。

函数

在Java中并没有函数这个概念,函数的功能是用静态方法来实现的。

函数的定义:

def 函数名(指明类型的参数): 可选的返回值类型 = 函数体
def fac(n: Int) = {
  var r = 1
  for (i <- 1 to n) r = r * i
  r
}

  

只要函数不是递归的,就不需要给出函数的返回值类型(因为有类型推断)。一个递归的函数需要如下写:

def fac(n: Int): Int = if (n <= 0) 1 else n * fac(n - 1)

  

在Scala中,并不时常使用return来返回值。

默认参数和带名参数

Java里似乎没有默认参数和带名参数,至少我从来没有使用过。但记得C++是有的。

def decorate(str: String, left: String = "[", right: String = "]") =
  left + str + right

  

上面的函数有两个带有默认值的参数left和right。如果不给出这两个参数,调用这个函数,会使用默认值。也可以给出自己的参数来替代默认的。

参数个数不够时,将会从后往前使用默认参数。

调用函数时,如果指定参数名,那么参数可以不按照参数列表的顺序来:

decorate("Hello", right = "]<<<")

从上面还可以知道,可以混用未命名参数和带名参数。

变长参数

def sum(args: Int*) = {
  var result = 0
  for( arg <- args) result += arg
  result
}

  

星号表示可接受变长参数列表。函数实际得到的参数是Seq类型的。

但是在调用变长参数函数时,并不能直接使用Seq类型的参数。要将一个Seq类型打散成单个的参数序列,使用 : _*。

def recursiveSum(args: Int*) = {
  if (args.length == 0) 0
  else args.head + recursiveSum(args.tail: _*)
}

  

过程(procedure)

过程的定义与函数很类似。函数体在花括号中,而且花括号前没有=号,返回值是Unit类型的,这就是过程了。(也可以写出=号,并且写明返回值是Unit类型来定义过程。)

通常,过程没有返回值,只是为了副作用而调用一个过程。

 

懒值

当val被声明为lazy时,初始化会被推迟到首次对其取值时。

lazy val words = io.Source.fromFile("/usr/share/dict/words").mkString

  

如果程序一直不妨问words,那么Scala也不会去打开那个文件。文件是在实际第一次使用words时被打开的。

懒值对开销较大的初始化语句十分有用,可以应对一些初始化问题比如循环依赖。还是开发懒数据结构的基础。

可以将懒值当成是介于val和def的中间状态:

  • val在被定义时即被取值
  • lazy val在首次使用时被取值
  • def在每次使用时取值

异常

 

Scala中异常机制与Java和C++中一样。但Scala中并没有受检异常,也就是不需要说明函数方法可能会抛出什么异常。

使用throw来抛出一个异常。throw表达式的类型是Nothing。

捕获异常:

try { 
  process(new URL("http://horstmann.com/fred-tiny.gif"))
} catch {
  case _: => MalformedURLException => println("Bad URL: " + url)
  case ex: IOException => ex.printStackTrace()
}

  

如果不需要使用捕获的异常对象,使用_来代替变量名(_是Scala中的通配符)。

同时也有try/finally语句,通常用来清理。可以组合成try/catch/finally语句

 

fRom: http://nerd-is.in/2013-08/scala-learning-control-structures-and-functions/

  

 

  

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值