Scala程序设计语言

  • 一函数式编程思想
    • 1 介绍
    • 2 scala函数式编程思想
  • 二Scala开发环境
    • 1 scala环境搭建
  • 三Scala语言基础
    • 1 scala基础语法之变量
    • 2 scala数据类型
    • 3 scala函数与代码块
    • 4 scala基础if与for
    • 5 scala基础try与match表达式
  • 四求值策略
    • 1求值策略
  • 五高阶函数
    • 1 Scala函数与匿名函数
      • 11 高阶函数
      • 12 匿名函数
    • 2 Scala柯里化
    • 3 Scala递归与尾递归
    • 4 例子
  • 六Scala Immutable Collection
    • 1 Scala Collections-list基本使用
    • 2 Scala list 高级使用
    • 3 Scala list-map
    • 4 Scala reduceLeft与flodLeft
    • 5 Scala Range 与 Stream
    • 6 Scala tuple与map
    • 7 Scala 快速排序案例

最近跟随慕课网课程《Scala程序设计语言》学习scala基础,随之笔记,以备后查。

一、函数式编程思想

1.1 介绍

  • Scala来源于Scalable编程语言,表明其是一种可伸缩的编程语言,既可以写非常小的脚本,又可以写非常复杂的服务器程序。
  • Scala是一种纯正的面向对象语言,它里面所有的概念最终都会被实现为对象。
  • 具备函数式编程的特性
  • 构建于JVM之上,可以与Java互操作。

1.2 scala函数式编程思想

  • 函数式编程:它是一种编程范式,它是一种构建计算机程序和结构的方法和风格,它把计算当做数学函数求值的过程,并且避免了改变状态和可变的数据。

  • 纯函数(Pure Function):或称函数的纯粹性(Purity),没有副作用(Side Effect)。

  • 副作用:指状态的变化(mutation)。

    val x = 1
    def XplusY_V1(y: Int) = x + y
    def XplusY_V2(y: Int) = {x += y; x }
    
    x
    > res0: Int = 1
    XplusY_V1(2)
    > res1: Int = 3
    x
    > res2: Int = 1
    
    XplusY_V1(2)
    > res3: Int = 3
    x
    > res4: Int = 3

    ​ 在上面的伪代码中,对于V1函数执行之后,x的值没有变化,即该函数对x不具备副作用,是个纯函数。V2反之。

  • 引用透明(Referential Transparency):对于相同的输入,总是得到相同的输出;如果 f(x) 的参数 x 和函数体都是引用透明的,那么函数 f 是纯函数。

    例子:违反引用透明,对于相同的输入,append方法返回不一样,即违反引用透明性。

    val x = new StringBuilder("Hello")
    > x : StringBuilder = Hello
    
    var y = x.append("World!")
    > y : StringBuilder = Hello World!
    
    var z = x.append("World!")
    > z : StringBuilder = Hello World! World!
  • 不变性(Immutability):为了获得引用透明性,任何值都不能变化。

  • 函数是一等公民(First-class Funtion):一切都是计算,函数式编程中只有表达式,变量、函数都是表达式

  • 表达式求值策略:Call By Value(属于 严格求值) 和 Call By Name(属于非严格求值);惰性求值(Lazy Evaluation,当定义表达式的时候,不会立即求值,第一次用到该表达式的时候才会求值)

  • 递归函数(Recursive Function):函数式编程中没有循环,所有的循环都是用递归实现的

  • 函数式编程的优点:

    • 生产效率高
    • 易于推理(Reasoning)
    • 适用于并行编程
    • 多核计算、云计算

二、Scala开发环境

2.1 scala环境搭建

  • JDK(建议java7以上)
  • Scala 2.11——下载msi版本的,可以自动配置环境变量,否则需要配置两个环境变量SCALA_HOME环境变量,指向Scala的安装目录。PATH环境变量,要包含 %SCALA_HOME%\bin的值。
  • SBT(Simple Build Tool):专为Scala设计的构建工具(也用msi安装版)。安装完成后打开SBT安装目录下的conf文件夹,修改stbconfig.txt如下
-Dfile.encoding=UTF8-Dsbt.boot.directory=d:/Code/SBT/boot/  -Dsbt.ivy.home=d:/Code/SBT/  -Xmx1536M  -Xss1M  -XX:+CMSClassUnloadingEnabled  -XX:MaxPermSize=256m  -XX:ReservedCodeCacheSize=128m  -Dsbt.log.format=true

​ 其中,-Dfile.encoding=UTF8-Dsbt.boot.directory=d:/Code/SBT/boot/和-Dsbt.ivy.home=d:/Code/SBT/两个参数表明jar包的存放位置(默认是放在系统盘用户目录下的.sbt和.ivy目录)

  • REPL(Read Evaluate Print Loop):轻量级的交互式程序运行环境,输入代码,立刻会将该段代码求值打印。(安装完了scala自带)

三、Scala语言基础

3.1 scala基础语法之变量

  • 三种变量修饰符

    val——定义immutable variable,常量

    var——定义mutable variable,变量

    lazy val——定义惰性求值的常量,一般,如果我们定义的变量,在后续程序中可能不会被用到,则可定义lazy val变量,该类型变量会在第一次用到的时候被求值,而var和val类型的变量则会在定义时直接求值。

    可以不显式指定变量的类型,因为Scala会自动进行类型推导

  val x = 10                                   //> x : Int = 10
  val y = 20                                   //> y : Int = 20
  //res0是scala为没有变量名的表达式取的默认名,在命令行中res可直接引用
  x + x                                        //> res0: Int = 20

  val z = x*y                                  //> z : Int = 200
  // z = x+y //会报错,val定义的值为常量,一旦确定,则不允许被改变
  var a = 200                                 //> a : Int = 200
  a = 300                                     //var定义的变量可以被修改

  val d = 20                                  //> d : Int = 20
  val e = 30                                  //> e : Int = 30
  lazy val f = d*e                            //> f: => Int
  f*10                                       //> res1: Int = 6000
  f                                          //> res2: Int = 600

3.2 scala数据类型

  • Any——AnyVal——Numeric types
  val a:Byte = 10                           //> a : Byte = 10
  val b:Short = 20                          //> b : Short = 20
  val c:Int = 30                            //> c : Int = 30
  val d:Long = 40                           //> d : Long = 40 
  val e:Float = 50                          //> e : Float = 50.0
  val f:Double = 60.98765                   //> f : Double = 60.98765
  val x:Long = b                            //> x : Long = 20 

​ 同Java一样,低精度向高精度赋值时,不需要类型转换,反之不可以。

  • Any——AnyVal——Boolean和char
  val m = true                              //> m : Boolean = true
  val n = false                             //> n : Boolean = false
  val q = 'X'                               //> q : Char = X

​ boolean型有true和false两种,字符型同java一样,用单引号表示。

  • Any——AnyVal——Unit

​ Unit类似Java中的void,往往作为函数的返回值类型出现,在函数式编程中,Unit往往表示函数是有副作用的,因为函数不返回任何的值

  //()是Unit类型的文字量
  val u:Unit=()                             //> u : Unit = ()
  val p = ()                                //> p : Unit = ()
  • Null与Nothing

    null的语义和java中null的语义是类似的,表示一个引用类型,他的值是空,在scala中通常不会使用null。

    nothing的语义是程序异常终止,对一个函数而言,如果返回值是nothing,表明该函数发生了异常。

  def foo() = throw new Exception("error occurred")      //> foo: ()Nothing
  • String

    构建于Java 的String之上,新增了字符串插值(interpolation)的特性 。

  val name="Sung_Lee"                       //> name : String = Sung_Lee 
  s"my name is ${name}"                     //> res0: String = my name is Sung_Lee

​ 用s标记开头,双引号文字量内用${}来包含已有的变量(上述的基本数据类型会当做字符串处理)。

3.3 scala函数与代码块

  • Block

    在scala中,代码块用于组织多个表达式,而代码块也是一个表达式,它最终求得的值是最后一个表达式的值

  • 函数

def functionName(param:ParamType,...):ReturnType={
    //function body:expressions
}

​ 上述是函数的基本定义格式,参数列表中,每个参数是”参数名:参数类型”,参数列表中参数可以有多个,中间用”,”隔开。

Eg.1

def hello(name: String): String = {
    s"Hello,${name}"
}                                               //> hello: (name: String)String
hello("lisong")                                 //> res1: String = Hello,lisong

Eg.2,scala可以自己推断出返回类型,可以将返回值类型省略

def hello(name: String)= {
    s"Hello,${name}"
}                                               //> hello: (name: String)String
hello("lisong")                                 //> res1: String = Hello,lisong

Eg.3

def add(x:Int,y:Int) = {
    x+y
}                                               //> add: (x: Int, y: Int)Int
add(1,2)                                        //> res2: Int = 3

3.4 scala基础if与for

  • if表达式
if(logical_exp) valA else valB

Eg.1

val o = 1                                       //> o : Int = 1
if(o == 1) o                                    //> res5: AnyVal = 1
if(a != 1) "not one"                            //> res6: Any = ()
if(a != 1) "not one" else o                     //> res7: Any = 1

​ 在上述代码中,第三句,a!=1为false,但又没有else语句,故该表达式的返回结果为空,在Scala中,空是Unit,即返回Unit的文字量”()”。

  • for comprehension

​ 用于实现循环的一种推导式,本质上是由scala的函数,像mapper、reduce组合而实现的。for是一种简写方式,也是scala语法糖的一种。

Eg.1

val l = List("alice","bob","cathy")             //> l : List[String] = List(alice, bob, cathy)

  for(
        s <- l  //generator,循环遍历l,每次迭代,把l中的一个元素给s
  ) println(s)                                    //> alice
                                                  //| bob
                                                  //| cathy

  for {
    s <- l
    if (s.length() > 3)  //filter,如果当前迭代的s的长度大于3,则输出,否则直接进行下次循环
  } println(s)                                    //> alice
                                                  //| cathy

  //yield,意为导出,循环中,若满足s1!="",则导出当前s1到一个新的collection中 
  val result_for = for{
    s <- l
    s1 = s.toUpperCase()  //variable binding
    if (s1 != "")
  } yield (s1)                             //> result_for : List[String] = List(ALICE, BOB, CATHY)

3.5 scala基础try与match表达式

  • try表达式

    它的语义同java中try、catch、finally的语义相同,但它不是一个语句,而是一个表达式,会返回一个值。

  try {
    Integer.parseInt("dog");
  } catch {
    case _ => 0     //"_"是通配符,他可以通配所有的对象,无论抛出什么异常,总能catch到,并返回0
  } finally {       //同java,无论有没有异常抛出,finally总会被执行
    println("aways be printed")
  }                                               //> aways be printed
                                                  //| res8: Int = 0
  • match表达式

    类似于Java中的switch

  exp match {   //主要用在pattern match中
    case p1 => val1
    case p2 => val2
    ...
    case _=> valn
}

​ val是表达式的返回值,当所有pattern都没有被匹配的时候,总会匹配”_“,返回valn,类似于Java中的default。

val code = 1                                    //> code : Int = 1
  val result_match = code match {   //主要用在pattern match中
    case 1 => "one"
    case 2 => "two"
    case _ => "others"
  }                                               //> result_match : String = one 

四、求值策略

4.1求值策略

  • Scala中有两种求值策略(Evaluation Strategy):

    1.Call By Value —— 对函数实参求值,且仅求值一次

    2.Call By Name —— 函数实参每次在函数体内被用到时都会求值

    Scala通常使用Caal By Value,如果过函数形参类型以 => 开头,那么会使用Call By Name

def foo(x: Int) = x     //call by value
def foo(x: => Int) = x  //call by name

Eg.1

def test1(x: Int,y: Int): Int = x * x               //> test1: (x: Int, y: Int)Int
def test2(x: => Int, y: => Int):Int = x*x       //> test2: (x: => Int, y: => Int)Int
test1(3+4,8)                                    //> res9: test1(7,8)——>7*7——>49
test2(3+4,8)                                    //> res10: test2(3+4,8)——>(3+4)*(3+4)——>7*7——>49

Eg.2

 def bar(x: Int, y: => Int): Int = 1             //> bar: (x: Int, y: => Int)Int
 def loop(): Int = loop                        //> loop: ()Int
 bar(1, loop)                                  //> res11: Int = 1
 bar(loop, 1)

​ 在第一次调用中,loop传给形参y,但bar函数体中一直没有用到y,且形参y是call by name的,故一直不会被求值。在第二次调用中,递归表达式loop传给形参x,但x是call by value的,会直接对实参loop求值,loop递归不停,故不能返回值。

五、高阶函数

5.1 Scala函数与匿名函数

  • 在Scala中,函数是第一等公民

    Scala语言支持:

    ​ 1.把函数作为实参传递给另外一个函数

    ​ 2.把函数作为返回值

    ​ 3.把函数赋值给变量

    ​ 4.把函数存储在数据结构里

    在Scala中,函数就像普通变量一样,同样也具有函数的类型。

  • 函数类型

​ 在Scala语言中,函数类型的格式为A => B ,表示一个接受 类型A的参数,并返回类型B 的函数。例如,Int => String是把整型映射为字符串的函数类型

5.1.1 高阶函数

​ 在Scala中,用函数作为形参或返回值的函数,称为高阶函数。

def operate(f: (Int, Int) => Int) = {
    f(4,4)
 }                                                //> operate: (f: (Int, Int) => Int)Int
def greeting() = (name:String) => {"hello" + " " + name}
                                                  //> greeting: ()String => String

​ 像第二个函数的定义方式称为匿名函数。

5.1.2 匿名函数

​ 匿名函数(Anonymous Function),就是函数常量,也称为函数文字量(Function Literal)。在Scala中,匿名函数的定义格式为:

​ (形参列表) => { 函数体 }

Eg.1

def function2(m: (Int) => Int) = m      //> function2: (m: Int => Int)Int => Int
def funint2(m: Int): Int = m               //> funint2: (m: Int)Int
function2(funint2)(2)                        //> res5: Int = 2

def function3(f: (Int, Int) => Int) = f       //> function3: (f: (Int, Int) => Int)(Int, Int) => Int
def funint3(m: Int,n: Int): Int= m*n       //> funint3: (m: Int, n: Int)Int
function3(funint3)(2,3)                        //> res6: Int = 6

Eg.2

 def operate(f: (Int, Int) => Int) = {
    f(4,4)
 }                                                //> operate: (f: (Int, Int) => Int)Int
 def add(x:Int,y:Int) = {
    x+y
  }                                               //> add: (x: Int, y: Int)Int
 def greeting() = (name:String) => {"hello" + " " + name}
                                                  //> greeting: ()String => String 
 (x: Int, y: Int) => {x+y}                        //> res11: (Int, Int) => Int = <function2>
 add(1,2)                                         //> res12: Int = 3
 greeting()("LS")                                 //> res13: String = hello LS
 operate(add)                                     //> res14: Int = 8

5.2 Scala柯里化

​ 柯里化函数(Curried Function)把具有多个参数的函数转换为一条函数链,每个节点上是单一参数。

Eg.1 一下两个add函数定义是等价的

 def add1(x:Int,y:Int) = x+y                      //> add1: (x: Int, y: Int)Int
 //下面是Scala中柯里化的语法
 def add2(x:Int)(y:Int) = x + y                   //> add2: (x: Int)(y: Int)Int

Eg.2

def curriedAdd(a:Int)(b:Int) = a + b               //> curriedAdd: (a: Int)(b: Int)Int
curriedAdd(2)(2)                                 //> res15: Int = 4
//"_"通配了第二个形参b,第一个形参给定为1(偏应用函数),估只有一个参数是灵活可变的
val addOne = curriedAdd(1) _                     //> addOne : Int => Int = <function1>
addOne(2)                                        //> res16: Int = 3

5.3 Scala递归与尾递归

  • 递归函数(Recursive Function)在函数式编程中是实现循环的一种技术

Eg.1

def factorial(n: Int): Int = {
    if(n <= 0) 1
    else n*factorial(n-1)
 }                                                //> factorial: (n: Int)Int
 factorial(4)                                     //> res17: Int = 24

​ 为了防止堆栈溢出,采取了尾递归的优化方案

  • 尾递归函数

    ​ 尾递归函数(Tail Recursive Function)中所有递归形式的调用都出现在函数的末尾。当编译器检测到一个函数调用是尾递归的时候,他就覆盖当前的活动记录而不是在栈中去创建一个新的。

Eg.2

@scala.annotation.tailrec
def factorial(n: Int,m: Int): Int={
   if (n<=0) m
   else factorial(n-1, m*n)
}                                               //> factorial: (n: Int, m: Int)Int 
factorial(5, 1)                                 //> res0: Int = 120

​ Scala对形式上严格的尾递归进行了优化,对于严格的尾递归,可以放心使用,不必担心性能问题。对于是否是严格尾递归,若不能自行判断, 可使用Scala提供的尾递归标注@scala.annotation.tailrec,这个符号除了可以标识尾递归外,更重要的是编译器会检查该函数是否真的尾递归,若不是,会导致编译错误。

​ 由于JVM的限制,对尾递归深层次的优化比较困难,因此,Scala对尾递归的优化很有限,它只能优化形式上非常严格的尾递归。也就是说,下列情况不在优化之列。

  • 如果尾递归不是直接调用,而是通过函数值。
//call function value will not be optimized
val func = factorialTailrec _
def factorialTailrec(n: BigInt, acc: BigInt): BigInt = {
if(n <= 1) acc
else func(n-1, acc*n)
}
  • 间接递归不会被优化 间接递归,指不是直接调用自身,而是通过其他的函数最终调用自身的递归。如下所示。
//indirect recursion will not be optimized
def foo(n: Int) : Int = {
if(n == 0) 0;
bar(n)
}
def bar(n: Int) : Int = {
foo(n-1)
}

5.4 例子

  • abf(x)

Eg.1 求解

def sum(f: Int => Int)(a: Int)(b: Int): Int = {
    @annotation.tailrec
    def loop(n: Int, acc: Int): Int = {
            if(n>b) {
                println(s"n=${n},acc=${acc}")
                acc
            }else{
                println(s"n=${n},acc=${acc}")
                loop(n + 1,acc+f(n))
            }
    }
    loop(a, 0)
  }                                               //> sum: (f: Int => Int)(a: Int)(b: Int)Int
 sum(x => x)(1)(5)                                //> n=1,acc=0
                                                  //| n=2,acc=1
                                                  //| n=3,acc=3
                                                  //| n=4,acc=6
                                                  //| n=5,acc=10
                                                  //| n=6,acc=15
                                                  //| res0: Int = 15
  //下面函数变量等价于调用sum(x => x*x)(1)(5)
  val sumsquar = sum(x => x * x) _                //> sumsquar : Int => (Int => Int) = <function1>
  sumsquar(1)(5)                                  //> n=1,acc=0
                                                  //| n=2,acc=1
                                                  //| n=3,acc=5
                                                  //| n=4,acc=14
                                                  //| n=5,acc=30
                                                  //| n=6,acc=55
                                                  //| res1: Int = 55

六、Scala Immutable Collection

6.1 Scala Collections-list基本使用

  • 定义一个数值型List
val a = List(1,2,3,4)                           //> a : List[Int] = List(1, 2, 3, 4)
  • 连接操作符”::”和”:::”,左边为元素,右边为List,将元素添加到List头部,从右到左连接
val b = 0 :: a                                  //> b : List[Int] = List(0, 1, 2, 3, 4)
val c = "x"::"y"::"z"::Nil                      //> c : List[String] = List(x, y, z)
//连接操作符:::,用于连接两个List,Any型是String(引用类型)和Int(值类型)的父类型
val d = a:::c                                   //> d : List[Any] = List(1, 2, 3, 4, x, y, z)
  • List第一个元素
a.head                                          //> res0: Int = 1
d.head                                          //> res1: Any = 1
c.head                                          //> res2: String = x
  • List的尾列表(除去第一个元素的剩下元素列表)
a.tail                                          //> res3: List[Int] = List(2, 3, 4)
c.tail                                          //> res4: List[String] = List(y, z)
  • 判断List是否为空
a.isEmpty                                       //> res5: Boolean = false
Nil.isEmpty                                     //> res6: Boolean = true
  • 遍历列表,并把Int型转为String型
def walkthru(l:List[Int]): String = {
    if(l.isEmpty) ""
    else l.head.toString()+" "+walkthru(l.tail)
  }                                               //> walkthru: (l: List[Int])String

  walkthru(a)                                     //> res7: String = "1 2 3 4 "

6.2 Scala list 高级使用

  • filter()函数
//与2取余数,若为奇数,则true,保留元素,否则去除
a.filter(x => x%2 ==1)                          //> res8: List[Int] = List(1, 3)
//字符串转为List
"boom".toList                                   //> res9: List[Char] = List(b, o, o, m)
//过滤只留数字 
"76 And Lakers".toList.filter(x => Character.isDigit(x))     //> res10: List[Char] = List(7, 6) 
  • takeWhile()函数
//其参数为boolean型的函数表达式,true继续take,false则停止
"76 And Lakers".toList.takeWhile {x => x!='L' } //> res11: List[Char] = List(7, 6, , A, n, d, )

6.3 Scala list-map

  • map函数,对List中每个元素进行映射,作为参数传入的函数定义了具体的映射关系
 c                                               //> res12: List[String] = List(x, y, z)
 c.map(x => x.toUpperCase())                     //> res13: List[String] = List(X, Y, Z)
 //"_"通配也是一样的道理,是匿名函数的简写方法
 c.map(_.toUpperCase())                          //> res14: List[String] = List(X, Y, Z)
 a.filter(_%2 == 1)                              //> res15: List[Int] = List(1, 3)
 //每个元素加10
 a.filter(_%2 == 1).map(_+10)                    //> res16: List[Int] = List(11, 13)
  • flatMap函数,将两层的List转换成一层
//q是一个List型的List
val q = List(a,List(4,5,6))        //> q : List[List[Int]] = List(List(1, 2, 3, 4), List(4, 5, 6))
//下面两个过滤是等价的,第二条语句对每一个List也用了通配
q.map(x => x.filter(_%2 == 0))     //> res17: List[List[Int]] = List(List(2, 4), List(4, 6))
q.map(_.filter(_%2 == 0))          //> res18: List[List[Int]] = List(List(2, 4), List(4, 6))
//将二维的List转成一维
q.flatMap (_.filter(_%2 == 0))     //> res19: List[Int] = List(2, 4, 4, 6)

6.4 Scala reduceLeft与flodLeft

  • reduceLeft

    reduceLeft( op : (T , T) => T ) ,op是我们自定义的操作

Eg.1 以下实现了两两op(这里是+)得到新结果,再与后面元素op,直至结束

a                                         //> res20: List[Int] = List(1, 2, 3, 4)
a.reduceLeft( (x,y) => x+y )              //> res21: Int = 10
a.reduce( _ + _ )                         //> res22: Int = 10
  • foldLeft

​ foldLeft( z : U ) (op: ( U , T ) => U ) ,z是进行规约的初始值,是U类型的,op是操作,foldLeft比reduceLeft更加通用,原因是,reduceLeft最终归约出来的值的类型是T类型,即我们输入集合的元素的类型。但foldLeft规约得到的类型跟初始值z是相同类型,即U。

Eg.2

a.foldLeft(0)(_ + _)                      //> res23: Int = 10
a.foldLeft(1)(_ * _)                      //> res24: Int = 24

6.5 Scala Range 与 Stream

  • Range,整数序列

Eg.1 定义一个Range

//to是一个闭区间,默认步长为1
1 to 5                 //> res25: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
//用by定义步长
1 to 10 by 2           //> res26: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
//将序列转化为List
(1 to 5).toList        //> res27: List[Int] = List(1, 2, 3, 4, 5)
//until也是定义序列的一种方式,需要注意的是until是一个前闭后开区间
1 until 5              //> res28: scala.collection.immutable.Range = Range(1, 2, 3, 4)
  • Stream,是一个lazy list,惰性求值列表

Eg.2 定义一个Stream

//惰性求值列表体现在,只有第一个元素是有值的,其余元素都是在用的时候才会求值
1 #:: 2 #:: 3 #:: Stream.empty     //> res29: scala.collection.immutable.Stream[Int] = Stream(1, ?)
val stream=(1 to 1000).toStream //> stream : scala.collection.immutable.Stream[Int] = Stream(1, ?)

​ 使用Stream惰性求值对程序的运行性能有帮助,即用到的时候求值,按需求值,可以避免内存溢出的异常。

Eg.3 访问Stream中的元素,与List访问元素方法类似

//访问Stream中的第一个元素
stream.head                       //> res30: Int = 1
//访问尾Stream(除去第一个元素外的剩余元素组成的stream),这里依然是惰性求值的特点
stream.tail                       //> res31: scala.collection.immutable.Stream[Int] = Stream(2, ?)

6.6 Scala tuple与map

  • Tuple,元组,相当于数据库里记录的概念。如果成员只有两个,则称之为对(pair)。
(1,2)                                  //> res32: (Int, Int) = (1,2)
1 -> 2                                 //> res33: (Int, Int) = (1,2)
val t = (1,"Alice","Math",95.5)        //> t : (Int, String, String, Double) = (1,Alice,Math,95.5)
//访问某个分量
t._1                                   //> res34: Int = 1
t._2                                   //> res35: String = Alice
t._3                                   //> res36: String = Math
t._4                                   //> res37: Double = 95.5

​ 如果想让一个函数返回多个值,则可以把多个值封装在一个tuple中(有点类似于Java中的类)

Eg.2

def sumSq(in: List[Int]):(Int,Int,Int) = {
    //第一个分量表示目标List长度,第二个分量是求和,第三个分量是求平方和
    in.foldLeft((0,0,0)) ((t,v) => (t._1+1,t._2+v,t._3+v*v))
}                                         //> sumSq: (in: List[Int])(Int, Int, Int)
sumSq(a)                                  //> res38: (Int, Int, Int) = (4,10,30)
  • Map [ K , V ]
//定义一个Map
val p = Map(1 -> "David" , 9 -> "Alex")   
        //> p : scala.collection.immutable.Map[Int,String] = Map(1 -> David, 9 -> Alex)
//通过key取value
p(1)                                  //> res39: String = David
p(9)                                  //> res40: String = Alex
//是否包含(取key判定)
p.contains(1)                         //> res41: Boolean = true
p.contains(2)                         //> res42: Boolean = false
//列出map中的所有key
p.keys                                //> res43: Iterable[Int] = Set(1, 9)
//列出map中的所有value
p.values                              //> res44: Iterable[String] = MapLike(David, Alex)
//添加一个[ k , v ] 
p + (8 -> "Ls")                       
        //> res45: scala.collection.immutable.Map[Int,String] = Map(1 -> David, 9 -> Alex, 8 -> Ls)
//删除key为1的[ k , v ] pair
p - 1                                
        //> res46: scala.collection.immutable.Map[Int,String] = Map(9 -> Alex)
//添加多个pair,用List存储
p ++ List(2 -> "Alice",5 -> "Bob")    
        //> res47: Map[Int,String] = Map(1 -> David, 9 -> Alex, 2 -> Alice, 5 -> Bob)
//删除多个pair,用List存储
p -- List(1,9,2)                      
        //> res48: scala.collection.immutable.Map[Int,String] = Map()
p ++ List(2 -> "Alice",5 -> "Bob") -- List(1,9) 
        //> res50: scala.collection.immutable.Map[Int,String] = Map(2 -> Alice, 5 -> Bob)

6.7 Scala 快速排序案例

Eg.1

def qSort(a : List[Int]) : List[Int] = {
  if(a.length < 2 ) a
  else{
    //下面List拼接用 “++” ,同“:::”效果相同
    qSort(a.filter( _ < a.head )) ++ a.filter( _ == a.head) ++ qSort( a.filter( _ > a.head ) )
  }
}                                      //> qSort: (a: List[Int])List[Int]
qSort(List(8,6,3,7,2,6,8,5,3,1,9))     //> res51: List[Int] = List(1, 2, 3, 3, 5, 6, 6, 7, 8, 8, 9)

你可能感兴趣的:(scala,spark,云计算,函数式编程,语言)