scala——(函数及抽象化,集合(Seq,Set,Map))

函数及抽象化

函数字面量及函数的定义

  1. Scala中不仅可以定义一个函数然后调用它,还可以写一个未命名的函数字面量,然后可以把它当成一个值传递到其它函数或是赋值给其它变量。
  2. 函数字面量体现了函数式编程的核心理念。字面量包括整数字面量、浮点数字面量、布尔型字面量、字
    符字面量、字符串字面量、符号字面量、函数字面量等。

什么是函数字面量呢?

  1. 在函数式编程中,函数可以像任何其他数据类型一样被传递和操作。函数的使用方式和其他数据类型的使用方式完全一致,可以像定义变量那样去定义一个函数,函数也会和其他变量一样,有类型有值;就像变量的“类型”和“值”是分开的两个概念一样,函数的“类型”和“值”也成为两个分开的概念;
  2. 函数的“值”,就是“函数字面量”。
scala> def add1(x: Int): Int = { x + 1 }
add1: (x: Int)Int
// 函数的类型为: (Int) => Int
// 输入参数列表只有一个括号,可以简写为: Int => Int
scala> def add2(x: Int, y: Int): Int = { x + y }
add2: (x: Int, y: Int)Int
// 函数的类型为: (Int, Int) => Int
scala> def add3(x: Int, y: Int, z: Int): Int = { x + y + z }
add3: (x: Int, y: Int, z: Int)Int
// 函数的类型为: (Int, Int, Int) => Int
scala> def add4(x: Int, y: Int, z: Int): (Int, Int) = { (x + y, y + z) }
add4: (x: Int, y: Int, z: Int)(Int, Int)
// 函数的类型为: (Int, Int, Int) => (Int, Int)
  1. 函数类型:(输入参数类型列表) => (输出参数类型列表),只有一个参数时,小括号可省略;函数体中只有1行语句时,大括号可以省略;

  2. 把函数定义中的类型声明部分去除,剩下的就是函数的“值”,即函数字面量:
    在这里插入图片描述

  3. 在Scala中这样定义变量: val 变量名: 类型 = 值 ;可以用完全相同的方式定义函数: val 函数名: 函数类型 = 函数字面量

    val add1: Int => Int = (x) => x+1
    val add2: (Int, Int) => Int = (x, y) => x + y
    val add3: (Int, Int, Int) => Int = (x, y, z) => x + y + z
    val add4: (Int, Int, Int) => (Int, Int) = (x, y, z) => (x + y, y + z)
    
  4. 在Scala中有自动类型推断,所以可以省略变量的类型 val 变量名 = 值 。同样函数也可以这样: val 函数名 = 函数字面量

    val add1 = (x: Int) => x + 1
    val add2 = (x: Int, y: Int) => x + y
    val add3 = (x: Int, y: Int, z: Int) => x + y + z
    val add4 = (x: Int, y: Int, z: Int) => (x + y, y + z)
    
  5. 函数的定义:

    val 函数名: (参数类型1,参数类型2) => (返回类型) = 函数字面量
    val 函数名 = 函数字面量
    函数字面量:(参数1:类型1,参数2:类型2) => 函数体
    val 函数名 = (参数1:类型1,参数2:类型2) => 函数体
    

函数与方法的区别

  1. 使用 val 定义的是函数(function),使用 def 定义的是方法(method)。二者在语义上的区别很小,在绝大多数情况下都可以不去理会它们之间的区别,但是有时候有必要了解它们之间的不同。

    scala> def addm(x: Int, y: Int): Int = x + y
    addm: (x: Int, y: Int)Int
    scala> val addf = (x: Int, y: Int) => x + y
    addf: (Int, Int) => Int = <function2>
    
  2. Scala中的方法与函数有以下区别:3

    1. Scala 中的方法与 Java 的类似,方法是组成类的一部分
    2. Scala 中的函数则是一个完整的对象。Scala 中用 22 个特质(从 Function1 到 Function22)抽象出了函数的概念
    3. Scala 中用 val 语句定义函数,def 语句定义方法
    // 下面用三种方式定义了函数,其中第二种方式最常见
    val adder1: (Int, Int) => Int = (x, y) => x+y
    val adder2 = (x: Int, y: Int) => x+y
    // Function2是特质,不能直接new
    // new Function2[Int,Int,Int]{ ... } 其实是定义并实例化一个实现了 Function2 特质的类的对象
    val adder3 = new Function2[Int, Int, Int]{
    	def apply(x: Int, y: Int): Int = {
    		x + y
    	}
    }
    
  3. 方法不能作为单独的表达式而存在,而函数可以;’

  4. 函数必须要有参数列表,而方法可以没有参数列表;

  5. 方法名是方法调用,而函数名只是代表函数对象本身;

  6. 在需要函数的地方,如果传递一个方法,会自动把方法转换为函数

// 方法不能作为单独的表达式而存在,而函数可以
scala> def addm(x: Int, y: Int): Int = x + y
addm: (x: Int, y: Int)Int
scala> val addf = (x: Int, y: Int) => x + y
addf: (Int, Int) => Int = <function2>
scala> addm
<console>:13: error: missing argument list for method addm
scala> addf
res8: (Int, Int) => Int = <function2>
// 函数必须要有参数列表,而方法可以没有参数列表
scala> def m1 = "This is lagou edu"
m1: String
// 函数必须有参数列表
scala> val f1 = () => "This is lagou edu"
f1: () => String = <function0>
// 方法名是方法调用
scala> m1
res16: String = This is lagou edu
// 函数名代表函数对象
scala> f1
res17: () => String = <function0>
// 这才代表函数调用
scala> f1()
res18: String = This is lagou edu
// 需要函数的地方,可以传递一个方法
scala> val list = (1 to 10).toList
lst: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> def double(x: Int) = x*x
double: (x: Int)Int
scala> list.map(double(_))
res20: List[Int] = List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
  1. 将方法转换为函数:一般情况下,不对二者做区分,认为都是函数,更多的时候使用def定义函数。

    scala> def f1 = double _ //注意:方法名与下划线之间有一个空格
    f1: Int => Int
    scala> f1
    res21: Int => Int = <function1>
    

匿名函数与占位符

  1. 函数没有名字就是匿名函数;匿名函数,又被称为 Lambda 表达式。
  2. Lambda表达式的形式为:(参数名1: 类型1, 参数名2: 类型2, … …) => 函数体
// 定义匿名函数
scala> (x: Int) => x + 1
res0: Int => Int = <function1>
// 函数没有名字,在集成开发环境中是无法被调用的
scala> res0(10)
res1: Int = 11
scala> val list = (1 to 10).toList
lst: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// 将匿名函数作为参数传递给另一个函数
scala> list.map((x: Int) => x + 1)
res2: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
// x一定是Int类型,这里可以省略
scala> list.map((x) => x + 1)
res3: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
// 只有一个参数,小括号可以省略
scala> list.map(x => x + 1)
res4: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
// 使用占位符简化函数字面量
scala> list.map(_ + 1)
res5: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
// 实现将List中的每个元素*2 + 1,但是出错了
scala> list.map(_ + _ + 1)
<console>:13: error: missing parameter type for expanded function ((x$1, x$2) =>
x$1.$plus(x$2).$plus(1))
// 这样是可行的
scala> list.map(2 * _ + 1)
// 通过reduce这个高阶函数,将list列表中的元素相加求和
scala> list.reduce((x,y) => x + y)
res0: Int = 55
// 使用占位符简化函数字面量
// 第一个下划线代表第一个参数,第二个下划线代表第二个参数
scala> list.reduce(_ + _)
res1: Int = 55

注意:多个下划线指代多个参数,而不是单个参数的重复运用

  1. 第一个下划线代表第一个参数
  2. 第二个下划线代表第二个参数
  3. 第三个……,如此类推

高阶函数

  1. 高阶函数:接收一个或多个函数作为输入 或 输出一个函数。
  2. 函数的参数可以是变量,而函数又可以赋值给变量,由于函数和变量地位一样,所以函数参数也可以是函数;
  3. 常用的高阶函数:map、reduce、flatMap、foreach、filter、count … … (接收函数作为参数)
package com.scala.part08

object HighFunction {
  def main(args: Array[String]): Unit = {
    //接收一个或多个函数作为输入的高阶函数
    val func = (n) => "*" * n
    (1 to 5).map(func(_)).foreach(println)

    //输出一个函数的高阶函数
    val URLBuilder = (ssl: Boolean, domainName: String) => {
      val schema = if (ssl) "https://" else "http://"
      //输出一个匿名函数
      (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
    }
    val domainName = "www.lagou.com"

    def getURl = URLBuilder(true, domainName)

    val endpoint = "show"
    val query = "id=1"
    val url = getURl(endpoint, query)
    println(url)

  }
}

闭包

  1. 闭包是一种函数,一种比较特殊的函数,它和普通的函数有很大区别:

    // 普通的函数
    val addMore1 = (x: Int) => x + 10
    // 外部变量,也称为自由变量
    var more = 10
    // 闭包
    val addMore2 = (x: Int) => x + more
    // 调用addMore1函数
    println(addMore1(5))
    // 每次addMore2函数被调用时,都会去捕获外部的自由变量
    println(addMore2(10))
    more = 100
    println(addMore2(10))
    more = 1000
    println(addMore2(10))
    
  2. 闭包是在其上下文中引用了自由变量的函数;

  3. 闭包引用到函数外面定义的变量,定义这个函数的过程就是将这个自由变量捕获而构成的一个封闭的函数,也可理解为”把函数外部的一个自由变量关闭进来“。

  4. 何为闭包?需满足下面三个条件:

    1. 闭包是一个函数
    2. 函数必须要有返回值
    3. 返回值依赖声明在函数外部的一个或多个变量,用Java的话说,就是返回值和定义的全局变量有关

柯里化

  1. 函数编程中,接收多个参数的函数都可以转化为接收单个参数的函数,这个转化过程就叫柯里化(Currying)。

  2. Scala中,柯里化函数的定义形式和普通函数类似,区别在于柯里化函数拥有多组参数列表,每组参数用小括号括起来。

  3. Scala API中很多函数都是柯里化的形式。

    // 使用普通的方式
    def add1(x: Int, y: Int) = x + y
    // 使用闭包的方式,将其中一个函数作为返回值
    def add2(x: Int) = (y:Int) => x + y
    // 使用柯里化的方式
    def add(x: Int)(y: Int) = x + y
    //调用柯里化函数add
    scala> add(1)(2)
    res1: Int = 3
    //add(1)(2)实际上第一次调用使用参数x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值。
    //实际上最先演变成这样的函数:def add(x: Int) = (y:Int) => x + y
    //在这个函数中,接收一个x为参数,返回一个匿名函数,这个匿名函数的定义是:接收一个Int型参数y,函数体是x+y。
    //调用过程如下:
    scala> val result=add(1)
    result: Int => Int = <function1>
    scala> val sum=result(2)
    sum: Int = 3
    scala> sum
    res0: Int = 3
    

部分应用函数(偏函数)

  1. 部分应用函数(Partial Applied Function)也叫偏应用函数,与偏函数从名称上看非常接近,但二者之间却有天壤之别。

  2. 部分应用函数是指缺少部分(甚至全部)参数的函数,如果一个函数有n个参数, 而为其提供少于n个参数, 那就得到了一个部分应用函数。

    // 定义一个函数
    def add(x:Int, y:Int, z:Int) = x+y+z
    // Int不能省略
    def addX = add(1, _:Int, _:Int)
    addX(2,3)
    addX(3,4)
    def addXAndY = add(10, 100, _:Int)
    addXAndY(1)
    def addZ = add(_:Int, _:Int, 10)
    addZ(1,2)
    // 省略了全部的参数,下面两个等价。第二个更常用
    def add1 = add(_: Int, _: Int, _: Int)
    def add2 = add _
    

偏函数

  1. 偏函数(Partial Function)之所以“偏”,原因在于它们并不处理所有可能的输入,而只处理那些能与至少一个 case 语句匹配的输入;

  2. 在偏函数中只能使用 case 语句,整个函数必须用大括号包围。这与普通的函数字面量不同,普通的函数字面量可以使用大括号,也可以用小括号;被包裹在大括号中的一组case语句是一个偏函数,是一个并非对所有输入值都有定义的函数;

  3. Scala中的Partial Function是一个trait,其类型为PartialFunction[A,B],表示:接收一个类型为A的参数,返回一个类型为B的结果。

    // 1、2、3有对应的输出值,其它输入打印 Other
    val pf: PartialFunction[Int, String] = {
    case 1 => "One"
    case 2 => "Two"
    case 3 => "Three"
    case _=> "Other"
    }
    pf(1) // 返回: One
    pf(2) // 返回: Two
    pf(5) // 返回: Other
    
  4. 通过偏函数实现过滤List中的String类型的元素,并将Int类型的元素加1。

    package com.scala.part08
    
    object PartialFunctionDemo {
      def main(args: Array[String]): Unit = {
        //[Any,Int],偏函数接收的数据类型是Any,返回的数据类型是Int
        val partialFunction = new PartialFunction[Any, Int] {
          //如果返回true,那么就调用apply方法;如果返回的是false,就过滤掉
          override def isDefinedAt(x: Any): Boolean = {
            println(x.toString)
            x.isInstanceOf[Int]
          }
    
          //对传入的整数值+1,并将其返回
          override def apply(v1: Any): Int = {
            println(v1.toString)
            v1.asInstanceOf[Int] + 1
          }
        }
        val list=List(10,"hadoop",20,"spark",30,"flink")
        list.collect(partialFunction).foreach(println(_))
    
      }
    }
    

集合

可变和不可变集合

  1. 根据容器中元素的组织方式和操作方式,可以分为有序和无序、可变和不可变等不同的容器类别;
  2. 不可变集合是指集合内的元素一旦初始化完成就不可再进行更改,任何对集合的改变都将生成一个新的集合;
  3. 可变集合提供了改变集合内元素的方法;
  4. Scala同时支持可变集合和不可变集合,主要下面两个包:
    1. scala.collection.mutable:定义了可变集合的特质和具体实现类
    2. scala.collection.immutable:定义了不可变集合的特质和具体实现类
  5. 对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本。Scala优先采用不可变集合,不可变集合元素不可更改,可以安全的并发访问。
  6. Scala集合有三大类:Seq(序列)、Set(集)、Map(映射);所有的集合都扩展自Iterable特质。

Seq(序列)

  1. Seq代表按照一定顺序排列的元素序列;该序列是一种特别的可迭代集合,包含可重复的元素;

  2. Seq元素的顺序是确定的,每个元素对应一个索引值;

  3. Seq提供了两个重要的子特质:

    1. IndexedSeq:提供了快速随机访问元素的功能,它通过索引来查找和定位的
    2. LinearSeq:提供了访问head、tail的功能,它是线型的,有头部和尾部的概念,通过遍历来查找。
  4. List代表元素顺序固定的不可变的链表,它是Seq的子类,List是函数式编程语言中典型的数据结构,与数组类似,可索引、存放类型相同的元素。

  5. List一旦被定义,其值就不能改变。

  6. List列表有头部和尾部的概念,可以分别使用head和tail方法来获取:

    1. head返回的是列表第一个元素的值
    2. tail返回的是除第一个元素外的其它元素构成的新列表
  7. Scala定义了一个空列表对象Nil,定义为List[Nothing],借助 Nil 可将多个元素用操作符 :: 添加到列表头部,常用来初始化列表;

  8. 操作符 ::: 用于拼接两个列表;

    // 构建List
    val lst1 = 1 :: 2:: 3:: 4 :: Nil
    // :: 是右结合的
    val lst2 = 1 :: (2:: (3:: (4 :: Nil)))
    // 使用 ::: 拼接List
    val lst3 = lst1 ::: lst2
    // 使用 head、tail获取头尾
    lst3.head //返回第一个元素
    lst3.tail //返回除第一个元素外的其它元素构成的新列表
    lst3.init //返回除最后一个元素外的其它元素构成的新列表
    lst3.last //返回最后一个元素
    
  9. 列表递归的结构,便于编写递归的算法:

    object ListDemo {
      def main(args: Array[String]): Unit = {
        //Nil表示一个空的列表
        //::操作符表示向集合中添加元素,它是从右往左进行运算的,所以集合对象一定要放在最右边
        val list1 = 1 :: 2 :: 3 :: 4 :: Nil
        val list2 = 5 :: 6 :: 7 :: 8 :: Nil
    
        //使用:::操作符进行了拼接,不要使用::进行列表的拼接,因为这样拼接的结果不是我们想要的
        val list3=list1:::list2
    
        println(list3.head) //返回第一个元素
        println(list3.tail) //返回除第一个元系之外的其他元素构成的新列表
        println(list3.init) //返回除最后一个元素之外的其他元素构成的新列表
        println(list3.last) //返回最后一个元素
    
        val list4=List(4,2,6,1,7,9)
        println(quickSort(list4))
      }
      def quickSort(list:List[Int]):List[Int]={
        list match {
          case Nil=>Nil
          case head::tail=>
            //通过partition将tail分为两部分
            //小于head的元素放入less列表中,大于head的元素放入greater列表中
            val (less,greater) =tail.partition(_<head)
            quickSort(less):::head::quickSort(greater)
        }
      }
    } 
    

Queue(队列)

  1. 队列Queue是一个先进先出的结构。队列是一个有序列表,在底层可以用数组或链表来实现。

  2. 先进先出的原则,就是先存入的数据,要先取出,后存入的数据后取出。

  3. 在Scala中,有scala.collection.mutable.Queue和scala.collection.immutable.Queue,一般来说,我们使用的是scala.collection.mutable.Queue

    package com.scala.part09
    
    import scala.collection.mutable
    
    object QueueDemo {
      def main(args: Array[String]): Unit = {
        //创建一个可变的队列
        val queue1=new mutable.Queue[Int]()
        println(queue1)
    
        //队列当中添加元素
        queue1 +=1
        //队列当中添加List列表
        queue1 ++=List(2,3,4)
        println(queue1)
    
        //按照进入队列的顺序,删除队列当中的元素
        //返回队列中的第一个元素,并从队列中删除这个元素
        val dequeue=queue1.dequeue()
        println(dequeue)
        println(queue1)
    
        //再向队列中添加元素
        queue1.enqueue(5,6,7)
        println(queue1)
    
        //获取第一个、最后一个元素
        println(queue1.head)
        println(queue1.last)
      }
    }
    

Set(集合)

  1. Set(集合)是没有重复元素的对象集合,Set中的元素是唯一的;

  2. Set分为可变的和不可变的集合;默认情况下,使用的是不可变集合(引用 scala.collection.immutable.Set);

  3. 使用可变集合,需要引用 scala.collection.mutable.Set 包;

    package com.scala.part09
    
    object SetDemo {
      def main(args: Array[String]): Unit = {
        //创建一个Set集合
        val set = Set(1, 2, 3, 4, 5, 6)
        set.drop(1)
        println(set)
    
        //创建一个可变的Set
        import scala.collection.mutable.Set
        val mutableSet = Set(3, 4, 5)
    
        //对可变的Set进行增加元素、删除元素的操作
        mutableSet.add(7)
        println(mutableSet)
    
        mutableSet.remove(7)
        println(mutableSet)
    
        //通过使用+=  -=进行增加、删除元素的操作
        mutableSet += 8
        mutableSet -= 3
        println(mutableSet)
    
        //对Set集合进行交集的操作(& intersect)
        println(Set(1, 2, 3) & Set(2, 3, 4))
        println(Set(1, 2, 3).intersect(Set(2, 3, 4)))
    
        //对Set集合进行并集的操作(++  |  union)
        println(Set(1,2,3) ++ Set(2,3,4))
        println(Set(1,2,3) | Set(2,3,4))
        println(Set(1,2,3).union(Set(2,3,4)))
    
        //对Set集合进行差集的操作(--  &~  diff)
        println(Set(1,2,3) -- Set(2,3,4))
        println(Set(1,2,3) &~ Set(2,3,4))
        println(Set(1,2,3).diff(Set(2,3,4)) )
      }
    }
    

Map(映射)

  1. Map(映射)是一系列键值对的容器;Scala 提供了可变的和不可变的两种版本的Map,分别定义在包 scala.collection.mutable 和 scala.collection.immutable 里;

  2. 默认情况下,Scala中使用不可变的 Map;如果要使用可变Map,必须导入scala.collection.mutable.Map;

  3. 在Map中,键的值是唯一的,可以根据键来对值进行快速的检索。

    package com.scala.part09
    
    import scala.collection.mutable
    
    object MapDemo {
      def main(args: Array[String]): Unit = {
        //使用两种方式定义Map
        val map1 = Map("a" -> 1, "b" -> 2)
        val map2 = Map(("a", 1), ("b", 2))
    
        map1.keys.foreach(println(_))
        map1.values.foreach(println(_))
    
        //如果访问不存在的Key值时,会抛出异常
        //    println(map1("c"))
    
        //也可以使用get方法,来获取与Key值相对应的Value值。
        //get方法会返回一个Option对象,要么要是Some(有值),要么是None(无值)
        val num: Option[Int] = map1.get("c")
        num match {
          case None => println("None")
          case Some(x) => println(x)
        }
    
        //获取Key值所对应的Value值,如果键Key不存在,那么就返回指定的默认值
        val num2: Int = map1.getOrElse("d", 0)
        println(num2)
    
        //创建一个可变的Map
    
        val map3 = scala.collection.mutable.Map("a" -> 1, "b" -> 2)
        println(map3)
        map3("a") = 10
        println(map3)
    
        //增加一个元素
        map3("c") = 3
        println(map3)
    
        //通过+=添加元素,-=删除元素
        map3 += ("d" -> 4, "f" -> 5)
        println(map3)
        map3 -= "d"
        println(map3)
    
        //将Key与Value的值互换
        val kv: mutable.Map[Int, String] = for ((k, v) <- map3) yield (v, k)
        println(kv)
        //推荐使用下面的方式将Key与value的值互换
        map3.map(x=>(x._2,x._1)).foreach(println(_))
    
        //通过拉链操作创建Map
        val a=Array(1,2,3)
        val b=Array("a","b","c")
        val c: Array[(Int, String)] = a.zip(b)
        val d: Map[Int, String] = a.zip(b).toMap
        println(d)
      }
    }
    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值