首页 > 代码库 > Scala的数据类型和基础语法

Scala的数据类型和基础语法


lang

2.1.     Java的异同

2.1.1.  语法

Java++:增加的语法

Java--:删减的语法

OO

静态成员

操作符重载

原生数据类型

closure

breakcontinue

使用trait进行mixin组合

接口

existential type_

通配符List<?>, import pkg.*;

抽象类型 type T

class C1[T] {...}

原始类型 class C1<T> {...}

模式匹配

enum枚举

注:

existential type——和Java互操作时进行对应

Iterator<? extends Component>  -->  Iterator[T] { type T <: Component }或者Iterator[_]

 

2.1.2.  

以下功能通过库的形式提供:

l  assert

l  enum

l  property

l  event

l  actor

l  resource control(自动释放)

l  query

2.2.     变量

2.2.1.  保留字

abstract   case       catch      class      def

do         else       extends    false      final

finally    for        if         implicit   import

match      new        null       object        override

package    private    protected requires   return

sealed        super      this       throw      trait

try        true       type       val        var

while      with       yield

_      :      =      =>     <-     <:     <%     >:     #      @

 

Scala调用Java的方法时,会碰到有Scala的保留字,如Thread.yield()

这在Scala中是非法的,专门有个解决办法,写成: Thread.`yield`()

 

注意:没有breakcontinue

2.2.2.  变量标识

这些标识在Java中是非法的,在Scala中是合法的,可以当作函数名使用,使接口更加DSL

val empty_? = true

val + = "hello"

val `yield` = 10

val ** = "power"

注意:可用但可读性不好


2.2.3.  变量定义

2.2.3.1     val, var

var 可变,可重新赋值,赋值为"_"表示缺省值(0, false, null),例如:

var d:Double = _ // d = 0.0

var i:Int = _ // i = 0

var s:String = _ // s = null

var t:T = _  // 泛型T对应的默认值

注意:仅 val可用"_"表示缺省值赋值。

 

val 不可变,相当于const/final但如果val为数组或者Listval的元素可以赋值

val pi = 3. // 相当于3.0d

val pi = 3.f // 相当于3.0f

 

提示:向函数式风格推进的一个方式,就是尝试不用任何var来定义变量。

2.2.3.2     花样定义

Python一样方便的赋值方式:

val x,y = 0 // 赋同一初始值

val (x,y) = (10, "hello") // 同时定义多个变量,注意:val x,y=10,"hello" 错误

更花:

val x::y = List(1,2,3,4)  // x = 1, y = List(2,3,4)

val List(a,b,c) = List(1,2,3) // a = 1, b = 2, c = 3

进一步花样:

val Array(ab_, _, c @ _*) = Array(123, 4, 5, 6, 7)  // 也可以用ListSeq

a // 1

b // 2

c // Array(5, 6, 7), _*匹配0个到多个这个也太花了吧!

 

使用正则表达式定义:

val regex = "(\\d+)/(\\d+)/(\\d+)".r

val regex(year, month, day) = "2010/1/13"

// year: String = 2010

// month: String = 1

// day: String = 13

2.2.3.3     lazy, val, def的区别

val

定义时就一次求值完成,保持不变

val f = 10+20 // 30 

lazy

定义时不求值,第一次使用时完成求值,保持不变

lazy f = 10+20 // <lazy>

f // 30

def

定义时不求值,每次使用时都重新求值

(无参,缺省返回值类型的函数定义)

def f = 10+20 // 30

def t = System. currentTimeMillis // 每次不一样

scala> val f1 = System.currentTimeMillis

f1: Long = 1279682740376    // 马上求值

scala> f1

res94: Long = 1279682740376 // 之后保持不变

scala> lazy val f2 = System.currentTimeMillis

f2: Long = <lazy>  // 定义时不求值

scala> System.currentTimeMillis

res95: Long = 1279682764297

scala> f2

res96: Long = 1279682766545 // 第一次使用时求值,注意:6545 > 4297

scala> f2

res97: Long = 1279682766545 // 之后保持不变

scala> def f3 = System.currentTimeMillis

f3: Long

scala> f3

res98: Long = 1279682784478 // 每次求值

scala> f3

res99: Long = 1279682785352 // 每次求值

2.3.     基本类型

尽量使用大写形式: Int, Long, Double, Byte, Short, Char, Float, Double, Boolean

编译时Scala自动对应到Java原始类型,提高运行效率。Unit对应javavoid

 

 asInstanseOf[T]方法来强制转换类型:

def i = 10.asInstanceOf[Double] // i: Double = 10.0

List(‘A‘,‘B‘,‘C‘).map(c=>(c+32).asInstanceOf[Char]) // List(‘a‘,‘b‘,‘c‘)

 

isInstanceOf[T]方法来判断类型:

val b = 10.isInstanceOf[Int] // true

而在match ... case 中可以直接判断而不用此方法。

 

Any统一了原生类型和引用类型。

 

2.3.1.  Int

-3 abs // 3

-3 max -2 // -2

-3 min -2 // -3

1.4 round // 1 四舍五入

1.6 round // 2 四舍五入

1.1 ceil // 2.0 天花板

1.1 floor // 1.0 地板

注:Rich Wrapper operations

 

++--操作,但可以+=, -=, 如下:

var i = 0

i++  // 报错,无此操作

i+=1 // 1

i--  // 报错,无此操作

i-=1 // 0

 

def even(n:Int) = 0==(n & 1)

def odd(n:Int) = !even(n)

2.3.2.  Char

String可以转化为List[Char]

String上做循环,其实就是对String中的每一个Char做操作,如:

"jamesqiu" max // ‘u‘

"jamesqiu" min // ‘a‘

(‘a‘ to ‘f‘) map (_.toString*3) // (aaa, bbb, ccc, ddd, eee, fff)

2.4.     BigInt

可以表示很大的整数:

BigInt(10000000000000000000000000) // 报错

BigInt("10000000000000000000000000") // scala.math.BigInt = 10000000000000000000000000

 

例如:

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

fac(1000)

或者写成:

def fac2(n:Int) = ((1:BigInt) to n).product

// res1: BigInt = 9332621544394415268169923885626670049071596826438......000000000000000000

2.5.     字符串

"..." 或者 """...""""

 

println("""|Welcome to Ultamix 3000.  

           |Type "HELP" for help.""".stripMargin)

输出:

Welcome to Ultamix 3000.  

Type "HELP" for help.

 

scala中,字符串除了可以+,也可以*

"abc" * 3 // "abcabcabc"

"abc" * 0 // ""

 

例子:

"google".reverse // "elgoog"

"abc".reverse.reverse=="abc" // true

 

例子:

"Hello" map (_.toUpper) // 相当于 "Hello".toUpperCase

2.5.1.  类型转换

"101".toInt // 101,无需 Integer.parseInt("101");

"3.14".toFloat // 3.14f

101.toString

3.14.toString

转换整个列表:

List("1","2","3") map (_.toInt) // List(1,2,3)

或者

List("1","2","3") map Integer.parseInt // List(1,2,3)

 

2.5.2.  StringBuilder

val sb = new StringBuilder

sb += ‘H‘

sb ++= "ello"

sb.toString // "Hello"

sb clear // StringBuilder()

2.5.3.  文本格式化

使用java.text.MessageFormat.format:

val msg = java.text.MessageFormat.format(
    "At {1,time} on {1,date}, there was {2} on planet {0}.",
    "Hoth", new java.util.Date(), "a disturbance in the Force")

输出

At 17:50:34 on 2010-7-20, there was a disturbance in the Force on planet Hoth.

 

方法2

"my name is %s, age is %d.format ("james", 30) // my name is james, age is 30.

 

注意:format还可以这么用

"%s-%d%1$s is %2$d.format ("james", 30) // james-30james is 30.

    "%2$d age‘s man %1$s: %2$dformat ("james", 30) // 30 age‘s man james: 30

 

2.6.     Null, None, Nil, Nothing

Null

Trait,其唯一实例为null,是AnyRef的子类,*不是* AnyVal的子类

Nothing

Trait,所有类型(包括AnyRefAnyVal)的子类,没有实例

None

Option的两个子类之一,另一个是Some,用于安全的函数返回值

Unit

无返回值的函数的类型,和javavoid对应

Nil

长度为0List

 

2.7.     ==eq

Scala==很智能,他知道对于数值类型要调用Java中的==ref类型要调用Javaequals()

"hello"=="Hello".toLowerCase()

java中为false,在scala中为true

 

Scala==总是内容对比

基本类型IntDouble

比值

其他类型

相当于A.equals(B)

eq才是引用对比

例如:

val s1,s2 = "hello"

val s3 = new String("hello")

s1==s2 // true

s1 eq s2 // true

s1==s3 // true 值相同

s1 eq s3 // false 不是同一个引用

2.8.     Option[T]

2.8.1.  概念

l  Option[T]可以是任意类型或者空,但一旦声明类型就不能改变;

l  Option[T]可完美替代Java中的null,可以是Some[T]或者None

l  Option实现了map, flatMap, and filter 接口,允许在 ‘for‘循环里使用它;

 

函数返回值能被统一处理了:

没有Option的日子

现在

def find(id:Long):Person = ...

def find(id:Long):Option[Person] = ...

返回Person或者null

返回Some[Person]或者None

返回null不特殊处理会抛:NullPointerExceptions

返回值直接getOrElse或者列表操作

类比:JavaStringx.split返回null

类比:JavaStringx.split返回new String[0]

结论:函数永远不要返回null值,如果输入有问题或者抛异常,返回Option[T]

 

参数有效性检查没有那么烦人了:

没有Option的日子

现在

def blank(s:String) = if (s==null) false else{

  s.toList.forall(_.isWhitespace) }

def blank(s:String) =Option(s).toList.forall(_.forall(_.isWhitespace))

结论:尽可能地不要浪费代码去检测输入,包装成Option[T]来统一处理

 

2.8.2.  使用

Some(3).getOrElse(4) // 3

None.getOrElse(4) // 4

例如打印key=3value

写法1

def p(map:Map[Int,Int]) = println(map(3))

p(Map(1->100,2->200)) // 抛异常

写法2

def p(map:Map[Int,Int]) = println(map get 3 getOrElse "...")

p(Map(1->100,2->200)) // ...

p(Map(1->100,3->300)) // 300

 

2.8.3.  例子

例子1

  def m(k:Int) = {

    Map((1,100),(2,200),(3,300)) get(kmatch {

      case Some(v) =>

        k + ": " + v

      case None =>

        "not found"

    }

  }

 

  def main(args : Array[String]) : Unit = {

    println(m(1)) // 100

    println(m(2)) // 200

    println(m(3)) // 300

    println(m(4)) // "not found"

    println(m(-1)) // "not found"

  }

 

例子2

val l = List(Some(100), None, Some(200), Some(120), None)

for (Some(s) <- l) yield s // List(100, 200, 120)

l flatMap (x=>x) // List(100, 200, 120)

 

例子3 Option结合flatMap

def toint(s:String) =

try { Some(Integer.parseInt(s)) } catch { case e:Exception => None }

List("123", "12a", "45") flatMap toint // List(123, 45)

List("123", "12a", "45") map toint // List(Some(123), None, Some(45))

 

2.9.     区分<-,=>,->

<-

for (i <- 0 until 100)

用于for循环, 符号的象形

=>

List(1,2,3).map(x=> x*x)

((i:Int)=>i*i)(5) // 25

用于名函数

也可用在import中定义别名:import javax.swing.{JFrame=>jf}

->

Map(1->"a",2->"b")

用于Map初始化也可以不用->而写成 Map((1,"a"),(2,"b"))

 

2.10.    match..case(switch)

2.10.1.      switch..case的区别

Java里面的写法

switch(n) {

    case(1): ...; break;

    case(2): ...; break;

    default: ...;

}

变成Scala写法

def m(n:String) =

match {

    case "a" | "b" => ... // 这个比较好

    case "c" => ...

    case _ => ...

}

匹配值 每个case..=>结束不用写break_相当于default

 

2.10.2.      匹配数据类型

match 可以很简单地匹配数据类型(不需要isInstanceOf[T]):

def f(v:Any) = v match {

    case null => "null"

    case i:Int => i*100

    case s:String => s

    case _ => "others"

}

注意:上面case中的is都叫模式变量

f(null) // "null"

f(5) // 500

f("hello") // "hello"

f(3.14) // "others"

 

注意:自定义类型如果也要匹配,需要用case class

2.10.3.      命令行参数解析例子

/** Basic command line parsing. */
object Main {
  var verbose = false  // 记录标识,以便能同时对-h-v做出响应
 
  def main(args: Array[String]) {
    for (a <- args) a match {
      case "-h" | "-help"    =>
        println("Usage: scala Main [-help|-verbose]")
      case "-v" | "-verbose" =>
        verbose = true
      case x => // 这里x是临时变量
        println("Unknown option: ‘" + x + "‘")
    }
    if (verbose) println("How are you today?")
  }
}

2.10.4.      使用case的递归函数

写法1

def fac(n:Int):Int = n match {

  case 0=>1

  case _=>n*fac(n-1)

}

 

写法2(使用映射函数):

def fac: Int=>Int = {

case 0=> 1

case n=> n*fac(n-1)

}

 

写法3(使用尾递归):

  def fac: (Int,Int)=>Int = {

    case (0,y) => y

    case (x,y) => fac(x-1, x*y)

  }
fac(5,1) // 120
 
写法4reduceLeft):
def fac(n:Int) = 1 to n reduceLeft(_*_)

implicit def foo(n:Int) = new { def ! = fac(n) }
5! // 120
 
写法5(最简洁高效)
  def fac(n:Int) = (1:BigInt) to n product
  fac(5) // 120

2.10.5.      变量匹配

常量匹配很简单,即case后跟的都是常量;
变量匹配需要注意,case后跟的是match里面的临时变量,而不是其他变量名:
3 match {
case i => println("i=" + i) // 这里i模式变量(临时变量),就是3
}
val a = 10
20 match { case a => 1 } // 1 a模式变量,不是10
为了使用变量a,必须用`a`:
        20 match { case `a` => 1; case b => -1 } // -1`a`是变量10
或者用大写的变量
        val A = 10
        20 match { case A => 1; case b => -1 } // -1,大写A是变量10
 

2.10.6.      case..if条件匹配

写法1
(1 to 20) foreach {                          

    case x if (x % 15 == 0) => printf("%2d:15n\n",x)     case x if (x % 3 == 0)  => printf("%2d:3n\n",x)     case x if (x % 5 == 0)  => printf("%2d:5n\n",x)     case x => printf("%2d\n",x)                          }
写法2
(1 to 20) map (x=> (x%3,x%5) match {
  case (0,0) => printf("%2d:15n\n",x)
  case (0,_) => printf("%2d:3n\n",x)
  case (_,0) => printf("%2d:5n\n",x)
  case (_,_) => printf("%2d\n",x)
})

2.11.    try..catch..finally

var f = openFile()

try {

  f = new FileReader("input.txt")

catch {

  case ex: FileNotFoundException => // Handle missing file

  case ex: IOException => // Handle other I/O error

finally {

  f.close()

}

2.12.    require

def f(n:Int) = { require(n!=0); 1.0/n }

def f(n:Int) = { require(n!=0, "n can‘t be zero"); 1.0/n }

f(0)

// java.lang.IllegalArgumentException: requirement failed: n can‘t be zero


require method takes one boolean parameter. If the passed value is true, require will return normally, otherwise, require throu an IllegalArgumentException

Scala的数据类型和基础语法