Scala学习笔记一

来源:互联网 发布:淘宝靠谱的数码店 编辑:程序博客网 时间:2024/05/29 13:19

Scala学习笔记一

第六步:for和foreach

java中循环数组输出:

for(i=0;i<str.length,;i++){    println(str[i]);}

scala中循环数组输出:

for(i<-str){    println(str(i))}

注意:scala的for中的i是val,虽然每次都被赋予了新值,但是每次循环时都是new一个新的对象,只是每次new的对象的名字都是i罢了。下面的7.3节和二十三章将再讲到for

又有如下代码:

//这里用的是tofor(i<- 0 to 2)  print(str(i))//这里用的是untilfor(i<- 0 until 3)  print(str(i))

  上面的代码中,to表示包含其后面的数组,即“0 to 2”表示“0,1,2”,而until不包含后面的数组,即“0 until 3”表示“0,1,2”。
  另外,上面的代码演示了scala的另一个通用规则:如果方法仅带一个参数,可以不带点或括号调用它。如本例中的to实际上是带一个Int参数的方法,“0 to 2”实际上应该是”(0).to(2)”。
  在scala中没有传统意义上的操作符,其中的“+,-,*,/”都可以用来做方法名。因此,“1+2”可以理解为“(1).+(2)”。示意图如下所示:

当然,在scala中,for也可以被写成foreach,例如上面的代码可以写成这样:

str.foreach((i:String)=>println(i))

上述代码中我们调用了str的foreach方法,其中i是传入的参数,String为i的类型,“->”右边是方法体。这里的String可以省略不写,编译器会推测为str数组的类型。

如果我们要显示的指定参数类型,那么必须要用小括号括起来,如果不指定类型,且只有一个参数,那么可以不写括号。
如果foreach中执行的函数仅由带一个参数的一句话组成,那么可以这么写:

str.foreach(println)

总结:scala函数文本的语法如下所示:
scala函数文本语法

第七步:Array

数组创建

//没有显示定义greeting的类型val greeting = new Array[String](3)//显示定义greeting的类型val greeting:Array[String] = new Array[String](3)//更简洁的形式,调用Array伴生对象的apply方法val greeting = Array("hello",",","world")

数组初始化

greeting(0)="hello"greeting(1)=","greeting(2)="world"

  scala中数组的访问用(),而不是java中的[]。因为在scala中,数组和其他类一样,都只是类的实现。因此当我们在一个或多个值或变量外使用小括号时,scala会把它转换成对名为“apply”方法的调用。即“greeting(0)”相当于“greeting.apply(0)”。
  与之相似的还有当对带有括号并包括一到若干参数的变量赋值时,编译器将把它转换成update方法的调用。即greeting(0) = “Hello”将被转化为greeting.update(0,”Hello”)
  对于“伴生对象”和“apply方法”将在4.3详细了解。
  另外,从上面可以看出,在scala中,当一个变量被定义为val时,那么这个变量不能被重新赋值,即这个变量永远只能指向它初始化时指向的对象,但是这个被指向的对象本身是可以变化的,例如上例中greeting可以被赋值。

第八步:List

List的创建与初始化

//下面两个方法创建的list类型都为Int//不用写成new List因为“List.apply()”是被定义在scala.List伴生对象上的工厂方法。val list = List(1,2,3)//或者用“::”和Nil来初始化val list = 1::2::3::Nil

  注意:scala中的list和java中的list是不同的,java中的list是可变的,即通过set()方法修改某个节点的值,但是scala中的list是不可变的,它是设计给函数式风格编程用的。因为函数式编程的哲学应用到对象世界里意味着对象不可变。
  数组:共享相同类型的可变对象序列;scala中的list:共享相同类型的不可变对象序列。
  list中有两个特别的方法,或者说是操作符:“::”和“:::”。”::”将左边的元素组合到右边list的最前端,然后返回一个新的list。”:::”将左右两个list叠加,返回一个新的list。

val list1 = List(1,2)val list2 = List(1,2)val list12 = list1:::list2val list11 = list1:::list1println(list12)//输出结果:List(1,2,1,2)println(list11)//输出结果:List(1,2,1,2)val list1 = List(1,2)val list12 = 1::list1println(list12)//输出结果:List(1,1,2)

  注意:由于list是不可变的,可以类比于java中的String,因此对于它的叠加或元素追加操作,都是将结果赋值给一个新的list并返回。
  

如果一个方法被用作操作符标注,如a * b,那么方法被左操作数调用,就像a.*(b);除非方法名以冒号结尾,这种情况下,方法被右操作数调用。因此,1 :: list1里,::方法被list1调用,传入1,像这样:list1.::(1)。5.8节中将描述更多操作符关联性的细节。

  另外,我们也发现,类List并没有提供append方法,因为随着列表长度增加,append耗时呈线性增长,使用“::”则仅花费常量时间。如果一定想通过添加元素来构造列表,可以把它们前缀进去,完成之后再调用reverse;或使用ListBuffer,一种提供append操作的可变列表,完成之后调用toList。ListBuffer将在22.2节中描述。

List常用的方法如下表所示:

方法吗名 方法作用 List() 或 Nil 空List List(“Cool”, “tools”, “rule) 创建带有三个值”Cool”,”tools”和”rule”的新List[String] val thrill = “Will”::”fill”::”until”::Nil 创建带有三个值”Will”,”fill”和”until”的新List[String] List(“a”, “b”) ::: List(“c”, “d”) 叠加两个列表(返回带”a”,”b”,”c”和”d”的新List[String]) thrill(2) 返回在thrill列表上索引为2(基于0)的元素(返回”until”) thrill.count(s => s.length == 4) 计算长度为4的String元素个数(返回2) thrill.drop(2) 返回去掉前2个元素的thrill列表(返回List(“until”)) thrill.dropRight(2) 返回去掉后2个元素的thrill列表(返回List(“Will”)) thrill.exists(s => s == “until”) 判断是否有值为”until”的字串元素在thrill里(返回true) thrill.filter(s => s.length == 4) 依次返回所有长度为4的元素组成的列表(返回List(“Will”, “fill”)) thrill.forall(s => s.endsWith(“1”)) 辨别是否thrill列表里所有元素都以”l”结尾(返回true) thrill.foreach(s => print(s)) 对thrill列表每个字串执行print语句(”Willfilluntil”) thrill.foreach(print) 与前相同,不过更简洁(同上) thrill.head 返回thrill列表的第一个元素(返回”Will”) thrill.init 返回thrill列表除最后一个以外其他元素组成的列表(返回List(“Will”, “fill”)) thrill.isEmpty 说明thrill列表是否为空(返回false) thrill.last 返回thrill列表的最后一个元素(返回”until”) thrill.length 返回thrill列表的元素数量(返回3) thrill.map(s => s + “y”) 返回由thrill列表里每一个String元素都加了”y”构成的列表(返回List(“Willy”, “filly”, “untily”)) thrill.mkString(“, “) 用列表的元素创建字串(返回”will, fill, until”) thrill.remove(s => s.length == 4) 返回去除了thrill列表中长度为4的元素后依次排列的元素列表(返回List(“until”)) thrill.reverse 返回含有thrill列表的逆序元素的列表(返回List(“until”, “fill”, “Will”)) thrill.sort((s, t) => s.charAt(0).toLowerCase < t.charAt(0).toLowerCase) 返回包括thrill列表所有元素,并且第一个字符小写按照字母顺序排列的列表(返回List(“fill”, “until”, “Will”)) thrill.tail 返回除掉第一个元素的thrill列表(返回List(“fill”, “until”))

第九步:Tuple

  元祖(tuple)是一种非常有用的容器,它是包含不同类型对象的不可变序列。如果我们需要在方法里返回多个对象。Java里你将经常创建一个JavaBean样子的类去装多个返回值,Scala里你可以简单地返回一个元组。而且这么做的确简单:实例化一个装有一些对象的新元组,只要把这些对象放在括号里,并用逗号分隔即可。一旦你已经实例化了一个元组,你可以用点号,下划线和一个基于1的元素索引访问它。
  
tuple的创建

//此时pair的类型为Tuple[Int,String,String]    val pair = (1,"left","123")

tupel的使用

println(pair._1)//输出1println(pair._2)//输出leftprintln(pair._3)//输出123

  注意,元祖的数字是从1开始的,这与list从0开始计数不同。
  尽管理论上你可以创建任意长度的元组,然而当前Scala库仅支持到Tupe22。

Array,List,Tuple的比较如下:

名称 是否可变 元素类型是否相同 Array 可变 必须相同 List 不可变 必须相同 Tuple 不可变 可以不同

第十步:Set和Map

Set

Scala中Set的类继承关系如下图所示:

  在scala中,通过继承将set分为可变和不可变两个分支(通过trait实现),它们共享同样的简化名,但是全称不一样,分别放在两个不同的包下面。

  set的创建与赋值

//调用Set伴生对象的名为apply的工厂方法var jetSet = Set("One","Two")jetSet += "Three"println(jetSet)//输出结果:Set(One,Two,Three)

  以上的代码定义了一个名为jetSet的var变量,Scala编译器推断jetSet的类型为不可变Set[String],于是返回了一个缺省的,不可变Set[String]的实例。
  上面的var和Set的不可变需要区分。var表示可以让jetSet指向其他的不可变Set[String]类型的对象,但是由于不可变Set本身是不可变的,因此不能对jetSet指向的原对象进行内容的修改。“jetSet+=Three”操作后返回的实际上是另外一个新对象,并让jetSet指向这个新对象。
  因此如果有下面的代码:

//调用Set伴生对象的名为apply的工厂方法val jetSet = Set("One","Two")jetSet += "Three"//这里会报错println(jetSet)

  那么编译时会出现错误,因为val规定了jetSet在创建了以后是不能再指向其他对象的,但是“jetSet += “Three”的执行结果是让jetSet指向新的Set对象,所以会报错。修改方法:将val改成var;或者在程序中引入可变的Set包“import scala.collection.mutable.Set”,如下所示:

//引入可变的Set包import scala.collection.mutable.Set//调用Set伴生对象的名为apply的工厂方法val jetSet = Set("One","Two")jetSet += "Three"println(jetSet)//输出结果:Set(One,Two,Three)

  再看如下代码:

var jetSet = Set("One","Two")jetSet += "One"jetSet += "Three"println(jetSet)//输出结果:Set(One, Two, Three)

  从上面看出,对于已经存在的元素,调用“+=”时会被忽略,不存在时才插入。
  

Map

Scala中Map的类继承关系如下图所示:
  

  Map和Set非常相似,采用了继承的机制提供了可变和不可变两种版本。
  Map的创建于赋值

import scala.collection.mutable.Map//方法一,此时“[Int,String]”是必须的val map = Map[Int,String]()map += (1 -> "one")map += (2 -> "two")map += (3 -> "three")println(map)//输出结果:Map(2 -> two, 1 -> one, 3 -> three)//方法二,此时编译器通过传入的值可以判断map的类型val map = Map(1->"one",2->"two",3->"three")println(map)//输出结果:Map(2 -> two, 1 -> one, 3 -> three)

  Scala编译器把如1 -> “one”这样的二元操作符表达式转换为(1).->(“one”)。因此,当你输入1 -> “one”,你实际上是在值为1的Int上调用->方法,并传入值为”one”的String。这个->方法可以调用Scala程序里的任何对象,并返回一个包含键和值的二元元组。然后你在把这个元组传递给map指向的Map的+=方法。最终,最后一行输出打印了map中的键值对。
  再看下面代码:

val map = Map(1->"One",2->"Two",3->"Three")map += (1 -> "zero")map += (4 -> "Four")println(map)//输出结果:Map(2 -> Two, 4 -> Four, 1 -> zero, 3 -> Three)

  从上面的输出结果可以看出:调用“+=”方法时,对于已经存在的key(如上例中的“1”),会对value进行修改,若key不存在,则会插入。

注意:scala之所以区分了可变和不可变,是为了充分利用函数式和指令式风格两方面的好处。

0 0