Scala学习笔记:重要语法特性

1.变量声明

Scala 有两种变量, val 和 var  val的值声明后不可变,var可变

val msg: String = "Hello yet again, world!"

或者类型推断

val msg = "Hello, world!"

2.函数定义

如果函数仅由一个句子组成,你可以可选地不写大括号。

def max2(x: Int, y: Int) = if (x > y) x else y

3.for循环

打印每一个命令行参数的方法是:

args.foreach(arg => println(arg))

如果函数文本由带一个参数的一句话组成,

args.foreach(println)

Scala 里只有一个指令式 for的函数式近似。

for (arg <- args)
   println(arg)

<- 的左边是变量,右边是数组。

再比如带类型的参数化数组

val greetStrings = new Array[String](3)
  greetStrings(0) = "Hello"
  greetStrings(1) = ", "
  greetStrings(2) = "world!\n"
  for (i <- 0 to 2)
    print(greetStrings(i))

注意这里的数组定义,只要new的时候带类型Array[String]就行了,val后面自动推断类型。

注意这里的数组索引用的是()而不是java里面的[]。

因为scala里面根本没有传统意义上的操作符,取而代之的是他们都可以转换为方法。例如greetStrings(i)可以转换成 greetStrings.apply(i),

greetStrings(0) = "Hello"  将被转化为 greetStrings.update(0, "Hello")

尽管实例化之后你无法改变 Array 的长度,它的元素值却是可变的。因此,Array 是可变的对象。

4.List对象

创建一个 List 很简单。 List里面元素不可变。

val oneTwoThree = List(1, 2, 3)

List有个叫“ :::”的方法实现叠加功能。

val oneTwo = List(1, 2)

val threeFour = List(3, 4)

val oneTwoThreeFour = oneTwo ::: threeFour

//结果是List(1, 2, 3, 4)

Cons 把一个新元素组合到已有 List的最前端,然后返回结果 List。 例如,若执行这个脚本:

val twoThree = list(2, 3)

val oneTwoThree = 1 :: twoThree

println(oneTwoThree)

//你会看到:
List(1, 2, 3)

一个简单的需记住的规则:如果一个方法被用作操作符标注,如 a* b,那么方法被左操作数调用,就像 a.*(b)——除非方法名以冒号结尾。这种情况下,方法被右操作数调用。因此, 1 :: twoThree 里, ::方法被 twoThree 调用,传入 1,像这样: twoThree.::(1)。

类 List 没有提供 append 操作,因为随着列表变长 append 的耗时将呈线性增长,而使用::做前缀则仅花费常量时间。如果你想通过添加元素来构造列表,你的选择是把它们前缀进去,当你完成之后再调用 reverse;

5.元组

与列表一样,元组也是不可变的,但与列表不同,元组可以包含不同类型的元素。

val pair = (99, "Luftballons", 55)
    println(pair._1)
    println(pair._2)
    println(pair._3)

注意这里第一个元素是从_1开始而不像List那样从0开始。

6.Set和Map

var jetSet = Set("Boeing", "Airbus")

jetSet += "Lear"

默认set或者HashSet可变。即jetSet = jetSet + "Lear"

如果要用不可变的set或者HashSet,要import

import scala.collection.immutable.HashSet

val hashSet = HashSet("Tomatoes", "Chilies")

println(hashSet + "Coriander")

//这里就不能再赋值给hashSet了

7.访问级别

Public 是 Scala 的缺省访问级别。C++中struct默认是public,class默认是private。java类中的变量默认是default类型,只允许在同一个包内访问,一般用的时候跟private差不多。所以java用的时候要想用public必须要指出。

class ChecksumAccumulator {

private var sum = 0

...

}

8.静态对象:object

Scala 比 Java 更面向对象的一个方面是 Scala 没有静态成员。替代品是, Scala 有单例对象: singleton object。除了用 object 关键字替换了 class 关键字以外,单例对象的定义看上去就像是类定义。

import scala.collection.mutable.Map

object ChecksumAccumulator {

private val cache = Map[String, Int]()

def calculate(s: String): Int =

if (cache.contains(s))

cache(s)

else {

.....

}

可以如下方式调用 ChecksumAccumulator单例对象的calculate方法:

ChecksumAccumulator.calculate("Every value is an object.")

也不用在实例化了。

类和单例对象间的一个差别是,单例对象不带参数,而类可以。因为你不能用new关键字实例化一个单例对象, 你没机会传递给它参数。

9.伴生对象

当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象: companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类: companion class。类和它的伴生对象可以互相访问其私有成员。

不与伴生类共享名称的单例对象被称为孤立对象: standalone object。 由于很多种原因你会用到它

10.Main函数

要执行Scala程序,你一定要提供一个有main方法(仅带一个参数, Array[String],且结果类型为 Unit的孤立单例对象名。比如下面这个例子;

import ChecksumAccumulator.calculate

object Summer {
     def main(args: Array[String]) {     //对比java里面的public static void

main(String[] args){}
         for (arg <- args)
         println(arg + ": " + calculate(arg))
     }

}

11.另一种Main函数

Application 特质:在单例对象名后面写上“ extends Application” 。然后取代main 方法.

import ChecksumAccumulator.calculate

object FallWinterSpringSummer extends Application {
   for (season <- List("fall", "winter", "spring"))
   println(season +": "+ calculate(season))

}

效果和main函数一样。不过它也有些缺点。首先, 如果想访问命令行参数的话就不能用它,因为args数组不可访问。第二,如果你的程序是多线程的就需要显式的 main 方法。最后,某些JVM的实现没有优化被 Application 特质执行的对象的初始化代码。因此只有当你的程序相对简单和单线程情况下你才可以继承 Application 特质。

12.带参数类声明

Java 类具有可以带参数的构造器,而 Scala 类可以直接带参数。 Scala 的写法更简洁——类参数可以直接在类的主体中使用;没必要定义字段然后写赋值函数把构造器的参数复制到字段里。(不需要构造函数)

例如以下分数构造器:

class Rational(n: Int, d: Int) {
   require(d != 0) 
   override def toString = n +"/"+ d

val numer: Int = n
   val denom: Int = d
  
   def add(that: Rational): Rational =      
   new Rational(
   numer * that.denom + that.numer * denom,
   denom * that.denom

}

require方法带一个布尔型参数。如果传入的值为真,require将正常返回。反之,require将通过抛出IllegalArgumentException来阻止对象被构造。
这里使用了重载。重载了类自带的toString函数。
这里that也就是随便起了个名字的变量,不是关键字。this是关键字。比如下面这个函数:

def lessThan(that: Rational) =

this.numer * that.denom < that.numer * this.denom

这里的this可以省略。但下面这个就不能省略了:

def max(that: Rational) =
  if (this.lessThan(that)) that else this

13.if返回值

Scala 的 if 可以产生值(是能返回值的表达式)。于是 Scala 持续了这种趋势让 for, try 和 match 也产生值。while不产生值,所以用得少。如果实在想用while,在纯函数式编程的时候可以考虑递归。

指令式风格:

var filename = "default.txt"

if (!args.isEmpty)

filename = args(0)

函数式风格:

val filename =

if (!args.isEmpty) args(0)

else "default.txt"

使用 val 而不是 var 的第二点好处是他能更好地支持等效推论。无论何时都可以用表达式替代变量名。

如要替代 println(filename),你可以这么写:

println(if (!args.isEmpty) args(0) else "default.txt")

14.break 和 continue

scala里面也没有break以及continue。如果想要用到他们的功能,可以使用增加布尔变量控制到循环语句判断中,类似:

var foundIt = false  while (i < args.length && !foundIt) { }

15.for过滤器

在for里面还支持过滤器:

val filesHere = (new java.io.File(".")).listFiles    //路径名的目录中的文件的数组。

for (file <- filesHere if file.getName.endsWith(".scala"))

println(file)

甚至多个过滤器:

for (
  file <- filesHere
  if file.isFile;
  if file.getName.endsWith(".scala")

) println(file)

16.for循环生成新集合

for {子句} yield {循环体} 制造新集合

例如:

def scalaFiles =

for {

file <- filesHere

if file.getName.endsWith(".scala")

} yield file

这样每一步就不是打印一个file,而是将file存储起来,最终产生一个Array[File]

17.try catch  finally

try {
     val f = new FileReader("input.txt")
     openFile(file)

} catch {
   case ex: FileNotFoundException =>  new FileReader("input.txt")
   //注意这里依然有返回值。使用=>符号
   case ex: IOException => // Handle other I/O error

}

finally {
   file.close() // 确保关闭文件。

}

这里catch {
   case ex: 。。。
   case ex: 。。。

}

对比java,catch是这样用的

catch (Exception e) { 。。。。} 而且经常在catch里面throw。scala一般不使用throw。

还有,finally里最好只做一些关闭或打印之类的操作,不要有副作用的表达式,这样会有无谓的返值。

18.switch语句

match语句就像java里的switch语句。

val firstArg = if (args.length > 0) args(0) else ""

firstArg match {

case "salt" => println("pepper")

case "chips" => println("salsa")

case "eggs" => println("bacon")

case _ => println("huh?")

}

差别:

1、Java 的 case 语句里面的整数类型和枚举常量。而这里可以是任意类型,甚至正则匹配

2、在每个可选项的最后并没有 break。取而代之, break是隐含的。

3、match 表达式也能产生值:例如可以这样:

val friend =

firstArg match {
  case "salt" => "pepper"
  case "chips" => "salsa"
  case _ => "huh?"

}

println(friend)

19.函数嵌套定义

函数:函数式编程风格的一个重要设计原则:程序应该被解构成若干小的函数, 每个完成一个定义良好的任务。在java中通常这样做:

def processFile(filename: String, width: Int) {
   ...
  for (line <- source.getLines)
   processLine(filename, width, line)

}

private def processLine(filename:String, width:Int, line:String) {
  ....

}

Scala 提供了另一种方式:你可以把函数定义在另一个函数中。就好象本地变量那样,这种本地函数仅在包含它的代码块中可见。

def processFile(filename: String, width: Int) {

def processLine(filename:String, width:Int, line:String) {
  ...

}

val source = Source.fromFile(filename)

for (line <- source.getLines) {

processLine(filename, width, line)

}

}

20.Lambda函数

Scala 拥有第一类函数: first-class function。你不仅可以定义函数和调用它们,还可以把函数写

成没有名字的文本: (跟python里面的lambda函数差不多)

scala> var increase = (x: Int) => x + 1

scala> increase(10)

res0: Int = 11

如果你想在函数文本中包括超过一个语句,用大括号包住函数体,当函数值被调用时,所有的语句将被执行,而函数的返回值就是最后一行产生的那个表达式。

scala> increase = (x: Int) => {
  println("We")
  println("are")
  println("here!")
  x + 1

}

21.集合通配符:_

Scala 提供了许多方法去除冗余信息并把函数文本写得更简短。比如去除参数类型以及被推断的参数之外的括号:

scala> someNumbers.filter(x => x > 0)

如果想让函数文本更简洁,可以把下划线当做一个或更多参数的占位符,只要每个参数在函数文

本内仅出现一次。 :

scala> someNumbers.filter(_ > 0)

还可以使用一个下划线替换整个参数列表。叫偏应用函数

scala> def sum(a: Int, b: Int, c: Int) = a + b + c

一般调用可以这样:

scala> sum(1, 2, 3)

用偏函数取而代之:

scala> val a = sum _  //请记住要在函数名和下划线之间留一个空格

scala> a(1, 2, 3)

再比如

someNumbers.foreach(println _)

22.闭包

闭包:是指可以包含自由(未绑定到特定对象)变量的代码块

任何带有自由变量的函数文本,如(x: Int) => x + more,都是开放术语:由于函数值是关闭这个开放术语(x: Int) => x + more 的行动的最终产物, 得到的函数值将包含一个指向捕获的 more 变量的参考, 因此被称为闭包。

scala> var more = 1

scala> val addMore = (x: Int) => x + more

scala> addMore(10)

res19: Int = 11

每次函数被调用时都会创建一个新闭包。每个闭包都会访问闭包创建时活跃的 more 变量。

23.重复参数

想要标注一个重复参数,在参数的类型之后放一个星号。例如:

scala> def echo(args: String*) =
        for (arg <- args) println(arg)

这样定义, echo 可以被零个至多个 String 参数调用:

scala> echo()

scala> echo("one")

scala> echo("hello", "world!")

但是如果有一个数组变量

scala> val arr = Array("What‘s", "up", "doc?"),则不能像scala> echo(arr)这样调用。

你需要在数组参数后添加一个冒号和一个_*符号,像这样:

scala> echo(arr: _*)

24.curry化

Scala 允许你创建新的“感觉像是原生语言支持”的控制抽象:scala提供curry 化:

Scala里的Curry化可以把函数从接收多个参数转换成多个参数列表。如果要用同样的一组实参多次调用一个函数,可以用curry化来减少噪音,让代码更有味道。我们要编写的方法不是接收一个参数列表,里面有多个参数,而是有多个参数列表,每个里面可以有一个或多个参数。也就是说,写的不是def foo(a: Int, b: Int, c: Int){},而是 def foo(a: Int)(b: Int)(c: Int){}。可以这样调用这个方法,比如:foo(1)(2)(3)、foo(1){2}{3},甚至这样foo{1}{2}{3}。

例如,传统函数如下:

scala> def plainOldSum(x: Int, y: Int) = x + y

scala> plainOldSum(1, 2)     //res5: Int = 3

scala允许你使用curry化的新型函数:

scala> def curriedSum(x: Int)(y: Int) = x + y

scala> curriedSum(1)(2)             //res5: Int = 3

结果一样。

25.带函数参数的函数

高阶函数: higher-order function——带其它函数做参数的函数

def filesMatching(query: String,
  matcher: (String, String) => Boolean) = {
  for (file <- filesHere; if matcher(file.getName, query))
    yield file

}

这里matcher其实是一个函数,这里做了filesMatching函数的参数。

(String, String) => Boolean)表示matcher函数的参数是(String, String)类型,而返回值是Boolean 类型

你可以通过让多个搜索方法调用它,并传入合适的函数:

def filesEnding(query: String) =
  filesMatching(query, _.endsWith(_))

这就相当于

def filesEnding(query: String) =
  for (file <- filesHere; if file.getName.endsWith(query))
   yield file

因为上面matcher: (String, String)里面两个参数, matcher(file.getName, query)

所以 _.endsWith(_)里面的第一个_对应于字符串file.getName,第二个_对应于字符串query,

连在一起就是file.getName.endsWith(query)

类似的

def filesContaining(query: String) =
  filesMatching(query, _.contains(_))

就相当于

def filesContaining(query: String) =
   for (file <- filesHere; if file.getName.contains(query))
    yield file

26.传名参数

Scala中允许无参数的函数作为另一函数的参数传递进去,也就是传名参数(call-by-name)

定义一个函数 myAssert ,而其参数则为传名参数。在这里,我们想要实现的是断言,可以将传名参数写成函数文本的格式:(…) => Type ,即参数列表 => 类型。

def myAssert(check: () => Boolean) =
       if(!check()){
              println("OK ...")
              throw new AssertionError
       }

上面的函数定义了一个当客户代码传入的函数值(这里我们用()指明,代表省略了该函数的参数列表。调用方式如下:

scala> myAssert(() => 5 < 3)

客户端代码中的 () => 5 < 3 似乎有点繁琐,如果能够直接传入 5 < 3 之类的布尔表达式就更好了。这是可以实现的。只需要将函数定义 def 中空参数列表即小括号对()去掉,直接用 => 而不是()=> 就可以了。此外,if 判断中的 check 后面的()也要同时去掉。修改后的代码如下:

def myAssert(check: => Boolean) =
       if(!check){
              println("OK ...")
              throw new AssertionError
       }

myAssert(5 < 3)

时间: 2024-09-30 17:36:48

Scala学习笔记:重要语法特性的相关文章

javascript学习笔记---ECMAScript语法(变量)

变量声明关键字var: var i = 1: var t = "asd"; var test1 = "hi", test2 = "hello"; 声明变量不一定要初始化, var i;//ok 另一方面在使用变量前若未加关键字var,则此变量为全局变量(此特性需特别记住). 变量名字: 变量名需要遵守两条简单的规则: 第一个字符必须是字母.下划线(_)或美元符号($) 余下的字符可以是下划线.美元符号或任何字母或数字字符 命名变量规则: Came

Scala学习笔记及与Java不同之处总结-从Java开发者角度

Scala与Java具有很多相似之处,但又有很多不同.这里主要从一个Java开发者的角度,总结在使用Scala的过程中所面临的一些思维转变. 这里仅仅是总结了部分两种语言在开发过程中的不同,以后会陆续更新一些切换后在开发过程中值得注意的地方.以下列举了部分,但令人印象深刻的Scala语言的不同之处,具体的代码演示样例及具体阐述见下文. ? Scala中可直接调用Java代码,与Java无缝连接. 语句能够不用";"结束.且推荐不适用";". 变量声明时以var或va

javascript学习笔记---ECMAScript语法(引用类型)

引用类型通常叫做类(class). 本教程会讨论大量的 ECMAScript 预定义引用类型. 引用类型 引用类型通常叫做类(class),也就是说,遇到引用值,所处理的就是对象. 本教程会讨论大量的 ECMAScript 预定义引用类型. 从现在起,将重点讨论与已经讨论过的原始类型紧密相关的引用类型. 注意:从传统意义上来说,ECMAScript 并不真正具有类.事实上,除了说明不存在类,在 ECMA-262 中根本没有出现"类"这个词.ECMAScript 定义了"对象定

javascript学习笔记---ECMAScript语法(辅助)

1.区分大小写. test与Test变量是两个不同的变量 2.变量是弱变量. 与C,java等等语言变量不是很一样. 如下java代码 ? 1 2 3 4 int i =0; //再次赋值 i = 10;//ok i = "hello";//wrong 类型不匹配 javascript ? 1 2 3 4 var i = 9; //再次赋值 i = 10;//ok i = "hello";//ok 但是不建议这么干,如此会给开发造成干扰,不明确变量的类型.(变量命名

原创:Scala学习笔记(不断更新)

Scala是一种函数式语言和面向对象语言结合的新语言,本笔记中就零散记下学习scala的一些心得,主要侧重函数式编程方面. 1. 以递归为核心控制结构. 实现循环处理的方式有三种:goto,for/while,递归,其中用goto实现循环已经在现代语言中被放弃,而for/while形式的结构化编程成为主流,而递归作为另一种方案,则长期只流行在函数式编程的小圈子中. 递归被主流编程界所担心的主要是过深的调用栈,甚至以前的课堂上我们还亲自尝试过将递归改写为循环,但是现代函数式编程语言中,通过尾递归(

Scala学习笔记及与Java不同之处总结-从Java开发人员角度

Scala与Java具有许多相似之处,但又有许多不同.这里主要从一个Java开发人员的角度,总结在使用Scala的过程中所面临的一些思维转变.这里只是总结了部分两种语言在开发过程中的不同,以后会陆续更新一些切换后在开发过程中值得注意的地方.下面列举了部分,但令人印象深刻的Scala语言的不同之处,具体的代码示例及详细阐述见下文. ? Scala中可直接调用Java代码,与Java无缝连接: 语句可以不用";"结束,且推荐不适用";": 变量声明时以var或val开头

Scala学习笔记一之基础语法,条件控制,循环控制,函数,数组,集合

前言:Scala的安装教程:http://www.cnblogs.com/biehongli/p/8065679.html 1:Scala之基础语法学习笔记: 1:声明val变量:可以使用val来声明变量,用来存放表达式的计算结果,但是常量声明后是无法改变它的值的,建议使用val来声明常量: 声明var变量:如果要声明可以改变的引用,可以使用var变量,声明的常量的值可以改变. 3:指定类型:无论声明val变量还是声明var变量.都可以手动指定其类型,如果不指定,scala会自动根据值,进行类型

Scala学习笔记-环境搭建以及简单语法

关于环境的搭建,去官网下载JDK8和Scala的IDE就可以了,Scala的IDE是基于Eclipse的. 下面直接上代码: 这是项目目录: A是scala写的: package first import scala.collection.mutable.ListBuffer object A { def main(args: Array[String]) { print("Hello,Scala");//学习程序设计的第一句 println("---");//pr

Scala学习笔记(二)

笔记的整理主要针对Scala对比Java的新特性:   1.if表达式 if表达式是有结果返回的. val a= if (5>2) "你好" else 1 a的值为if表达式返回值为 "你好"   2.while表达式 while表达式是没有返回值的(返回值为 Unit),在scala中避免使用,通常都需要与var结合使用   3.for表达式 枚举集合遍历 val a = Array(1,2,3,4,5,6) for (i <- a) println(

Scala学习笔记(四)(函数相关)

Scala函数问题: 函数是一组一起执行任务的语句.可以将代码放到独立的功能.如何划分你的代码不同功能?在逻辑上,通常是让每个函数执行特定的任务. Scala有函数和方法,我们术语说的方法和函数互换用微小的差别.Scala方法是其中有一个名字,签名,任选一些注释,有的字节码, 其中如在Scala中函数是可被分配给一个变量的完整对象类的一部分.换句话说,函数,其被定义为某些对象的一个成员,被称为方法. 函数定义可以出现在在源文件的任何地方,Scala允许嵌套函数的定义,那就是其他函数定义的内部函数