scala模式匹配

一,Scala模式匹配介绍

模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其组成部分。它是Java中的switch语句的升级版,同样可以用于替代一系列的if/else语句。

模式匹配语法中,采用 match 关键字声明,每个分支采用 case 关键字进行声明,当需要匹配时,会从第一个 case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有 case 都不匹配,那么会执行 case _分支,类似于 Java 中 default 语句。

package com.cw.chapter11

import scala.collection.GenTraversableOnce

object Scala01_Match {
  def main(args: Array[String]): Unit = {
    // 模式匹配,类似于Java的switch语法
    /*
      模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,
      当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,
      如果匹配不成功,继续执行下一个分支进行判断。
      如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句。
     */

    // TODO match的细节和注意事项
    //  1) 如果所有case都不匹配,那么执行case _ 分支,类似于Java中default语句
    //  2) 如果所有case都不匹配,又没有写case _ 分支,那么会抛出MatchError
    //  3) 每个case中,不用break语句,自动中断case
    //  4) 可以在match中使用其它类型,而不仅仅是字符,可以是表达式
    //  5) => 类似于 java swtich 的 :
    //  6) => 后面的代码块到下一个case, 是作为一个整体执行,可以使用{} 括起来,也可以不括。

    val oper = '*'
    val n1 = 20
    val n2 = 10
    var res = 0
    oper match {
      case '+' => res = n1 + n2
      case '-' => res = n1 - n2
      case '*' => res = n1 * n2
      case '/' => res = n1 / n2
      case _ => println("oper error")
    }
    println("res=" + res)

  }
}

二,模式守卫

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫,也就是在模式后面加上if <boolean expression>

def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
  notification match {
    case Email(sender, _, _) if importantPeopleInfo.contains(sender) =>
      "You got an email from special someone!"
    case SMS(number, _) if importantPeopleInfo.contains(number) =>
      "You got an SMS from special someone!"
    case other =>
      showNotification(other) // nothing special, delegate to our original showNotification function
  }
}

val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com")

val someSms = SMS("867-5309", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!")
val importantSms = SMS("867-5309", "I'm here! Where are you?")

println(showImportantNotification(someSms, importantPeopleInfo))
println(showImportantNotification(someVoiceRecording, importantPeopleInfo))
println(showImportantNotification(importantEmail, importantPeopleInfo))
println(showImportantNotification(importantSms, importantPeopleInfo))

case Email(sender, _, _) if importantPeopleInfo.contains(sender)中,除了要求notificationEmail类型外,还需要sender在重要人物列表importantPeopleInfo中,才会匹配到该模式。
案例2

object TestMatchGuard {
	def main(args: Array[String]): Unit = {
		 def abs(x: Int) = x match {
			 case i: Int if i >= 0 => i
			 case j: Int if j < 0 => -j
			 case _ => "type illegal"
		 }
		println(abs(-5))
	} 
}

案例2

val a = StdIn.readInt()

a match {
    case _ if a >= 0 && a <= 3 => println("[0-3]")
    case _ if a >= 4 && a <= 8 => println("[3-8]")
    case _ => println("未匹配")
}

二,典型的模式匹配场景

2.1 匹配字符串

Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。

object Test01 {
  def main(args: Array[String]): Unit = {
    val arr=Array("aa","bb","cc")
    //随机获取数组的任意元素
    val index=Random.nextInt(3)
    val value=arr(index)
    //模式匹配
    value match{
      case "aa" => println("0")
      case "bb" => println("1")
      case "cc" => println("2")
      case _ => println("null")  //表示所有的都没有匹配到执行这里
    }
  }
}

案例2

package com.cw.chapter11

object Scala03_Match1 {
  def main(args: Array[String]): Unit = {
    for (ch <- "+-3!") {
      var sign = 0
      var digit = 0
      ch match {
        case '+' => sign = 1
        case '-' => sign = -1
        case _ if ch.toString.equals("3") => digit = 3
        case _ => sign = 2
      }
      println(ch + " " + sign + " " + digit)
    }

    // 如果在case关键字后跟变量名,那么match前表达式的值会赋给那个变量
    val ch = 'V'
    ch match {
      case '+' => println("ok~")
      case mychar => println("ok~" + mychar)
      case _ => println("ok~~")
    }


  }
}

案例3

object TestMatchVal {
	 def main(args: Array[String]): Unit = {
	 	println(describe(6))
	 }
	 def describe(x: Any) = x match {
		 case 5 => "Int five"
		 case "hello" => "String hello"
		 case true => "Boolean true"
		 case '+' => "Char +"
	 } 
 }

2.2 匹配类型

需要进行类型判断时,可以使用isInstanceOf[T]asInstanceOf[T],也可使
用模式匹配实现同样的功能。

object Test01 {
  def main(args: Array[String]): Unit = {
    val arr=Array("aa",18,4.4,true)
    //随机获取数组的任意元素
    val index=Random.nextInt(arr.length)
    val value=arr(index)
    //模式匹配
    value match{
      case x:Int if(x>3) => println("Int")
      case y:String  => println("String")
      case z:Double => println("Double")
      case flag:Boolean => println("Boolean")
      case _ => println("null")  //表示所有的都没有匹配到执行这里
    }
  }
}

案例二

package com.cw.chapter11

object Scala04_Match2 {
  def main(args: Array[String]): Unit = {
    // 可以匹配对象的任意类型,这样做避免了使用isInstanceOf和asInstanceOf方法

    // 类型匹配, obj 可能有如下的类型
    val a = 7
    val obj = if (a == 1) 1
    else if (a == 2) "2"
    else if (a == 3) BigInt(3)
    else if (a == 4) Map("aa" -> 1) // Map[String, Int]
    else if (a == 5) Map(1 -> "aa") // Map[Int, String]
    else if (a == 6) Array(1, 2, 3) // Array[Int]
    else if (a == 7) Array("aa", 1) // Array[Any]
    else if (a == 8) Array("aa") // Array[String]

    // 类型匹配时,泛型是不起作用的,Array[Int]并不是代表泛型,只是表示数据类型的规约
    val result = obj match {
      case a: Int => a
      case b: Map[String, Int] => "对象是一个字符串-数字的Map集合"
      case c: Map[Int, String] => "对象是一个数字-字符串的Map集合"
      case d: Array[String] => "对象是一个字符串数组" // 底层转为 new String[];
      case e: Array[Int] => "对象是一个数字数组"  // 底层转成new int[];
      case f: BigInt => Int.MaxValue
      case g: Array[Any] => "Any"
      case _ => "啥也不是"
    }

    println(result)

    // 在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错.


  }
}

案例3

abstract class Device
case class Phone(model: String) extends Device {
  def screenOff = "Turning screen off"
}
case class Computer(model: String) extends Device {
  def screenSaverOn = "Turning screen saver on..."
}

def goIdle(device: Device) = device match {
  case p: Phone => p.screenOff
  case c: Computer => c.screenSaverOn
}

当不同类型对象需要调用不同方法时,仅匹配类型的模式非常有用,如上代码中goIdle函数对不同类型的Device有着不同的表现。一般使用类型的首字母作为case的标识符,例如上述代码中的p和c,这是一种惯例。

2.3 匹配数组、元组、集合

scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素
为 0 的数组。

object Test01 {
  def main(args: Array[String]): Unit = {
    //匹配数组
    val arr3=Array(0,1,2,3,4,5)
    arr3 match{
      case Array(0,x,y) => println(x+"--"+y) //匹配以0开头,后面两个元素的数组
      case Array(0) => println("only 0")     //匹配只有一个元素的数组
      case Array(0,_*) => println("many") //匹配以0开头的数组,_表示任意元素,*表示一到多个元素
    }

    //匹配序列
    val lst1=List(3,1,-1)
    lst1 match{
      case 0::Nil => println("only 0")    //匹配只有一个元素0的序列
      case x::y::Nil =>println(x+"--"+y) //匹配有两个元素的序列
      case 0::tail => println("0 is head ") //匹配以0开头的序列
      case _ => println("nothing ")
    }

    //匹配元组
    val tuple=(2,3,4)
    tuple match {
      case (1,x,y) => println("1 is head ") //匹配以1开头的 3个元素的tuple
      case (_,x,y) => println("many")       //匹配以任意开头的 3个元素的tuple
      case _ => println("nothing ")
    }
  }
}

运行结果

many
nothing 
many

Spark源码中模式匹配场景

package org.apache.spark.deploy.yarn

import scala.collection.mutable.ArrayBuffer

// TODO: Add code and support for ensuring that yarn resource 'tasks' are location aware !
private[spark] class ClientArguments(args: Array[String]) {

  var userJar: String = null
  var userClass: String = null
  var primaryPyFile: String = null
  var primaryRFile: String = null
  var userArgs: ArrayBuffer[String] = new ArrayBuffer[String]()

  parseArgs(args.toList)

  private def parseArgs(inputArgs: List[String]): Unit = {
    var args = inputArgs

    while (!args.isEmpty) {
      args match {
        // 匹配以 "--jar" 开头,后面至少一个值 的参数
        // tail就是其他的意思,这种结构连到一起就是匹配上的,
        // 如果args= "--jar","jars1"则会被匹配上,value则取值就是jars1
        case ("--jar") :: value :: tail =>
          userJar = value
          args = tail // 

        case ("--class") :: value :: tail =>
          userClass = value
          args = tail

        case ("--primary-py-file") :: value :: tail =>
          primaryPyFile = value
          args = tail

        case ("--primary-r-file") :: value :: tail =>
          primaryRFile = value
          args = tail

        case ("--arg") :: value :: tail =>
          userArgs += value
          args = tail

        case Nil =>

        case _ =>
          throw new IllegalArgumentException(getUsageMessage(args))
      }
    }

    if (primaryPyFile != null && primaryRFile != null) {
      throw new IllegalArgumentException("Cannot have primary-py-file and primary-r-file" +
        " at the same time")
    }
  }


}

MasterArguments参数解析的时候也有这么一段:

 private def parse(args: List[String]): Unit = args match {
 	// 匹配以 "--ip"或者"-i" 开头,后面至少一个值 的参数
    case ("--ip" | "-i") :: value :: tail =>
      Utils.checkHost(value)
      host = value
      parse(tail)

    case ("--host" | "-h") :: value :: tail =>
      Utils.checkHost(value)
      host = value
      parse(tail)

    case ("--port" | "-p") :: IntParam(value) :: tail =>
      port = value
      parse(tail)

    case "--webui-port" :: IntParam(value) :: tail =>
      webUiPort = value
      parse(tail)

    case ("--properties-file") :: value :: tail =>
      propertiesFile = value
      parse(tail)

    case ("--help") :: tail =>
      printUsageAndExit(0)

    case Nil => // No-op

    case _ =>
      printUsageAndExit(1)
  }

2.4 匹配对象

2.4.1 apply和unapply方法介绍

提取器对象是一个包含有unapply方法的单例对象。apply方法就像一个构造器,接受参数然后创建一个实例对象,反之unapply方法接受一个实例对象然后返回最初创建它所用的参数。提取器常用在模式匹配和偏函数中。

import scala.util.Random

object CustomerID {

  def apply(name: String) = s"$name--${Random.nextLong}"

  def unapply(customerID: String): Option[String] = {
    val stringArray: Array[String] = customerID.split("--")
    if (stringArray.tail.nonEmpty) Some(stringArray.head) else None
  }
}

val customer1ID = CustomerID("Sukyoung")  // Sukyoung--23098234908
customer1ID match {
  case CustomerID(name) => println(name)  // prints Sukyoung
  case _ => println("Could not extract a CustomerID")
}

这里apply方法用name创建一个CustomerID字符串。而unapply方法正好相反,它返回name。当我们调用CustomerID("Sukyoung"),其实是调用了CustomerID.apply("Sukyoung")的简化语法。当我们调用case CustomerID(name) => println(name),就是在调用提取器方法。

因为变量定义可以使用模式引入变量,提取器可以用来初始化这个变量,使用unapply方法来生成值。

val customer2ID = CustomerID("Nico")
val CustomerID(name) = customer2ID
println(name)  // prints Nico

上面的代码等价于val name = CustomerID.unapply(customer2ID).get

val CustomerID(name2) = "--asdfasdfasdf"

如果没有匹配的值,会抛出scala.MatchError

val CustomerID(name3) = "-asdfasdfasdf"

unapply 方法的返回值应当符合下面的某一条:

  • 如果只是用来判断真假,可以返回一个 Boolean 类型的值。例如 case even()。
  • 如果只是用来提取单个 T 类型的值,可以返回 Option[T]。
  • 如果你想要提取多个值,类型分别为 T1,…,Tn,可以把它们放在一个可选的元组中 Option[(T1,…,Tn)]。

有时,要提取的值的数量不是固定的,因此我们想根据输入来返回随机数量的值。这种情况下,你可以用unapplySeq方法来定义提取器,此方法返回Option[Seq[T]]。常见的例子有,用case List(x, y, z) => 来解构一个列表 List,以及用一个正则表达式 Regex 来分解一个字符串 String,例如 case r(name, remainingFields @ _*) =>

2.4.2 对象匹配细节介绍

package com.cw


// TODO 对象匹配
// 对象匹配,什么才算是匹配呢?规则如下:
// 1) case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功
// 2) 返回none集合则为匹配失败

// 1.构建对象时apply会被调用 ,比如 val n1 = Square(5)
// 2.当将Square(n)写在case后时[case Square(n) => xxx],会默认调用unapply方法(对象提取器)
// 3.number 会被 传递给def unapply(z: Double) 的 z 形参
// 4.如果返回的是Some集合,则unapply提取器返回的结果会返回给n这个形参
object Square {
  // 当对对象进行匹配时,会自动调用unapply方法
  def unapply(z: Double): Option[Double] = Some(math.sqrt(z))
  //def unapply(z: Double): Option[Double] = None

  // 构建对象时apply会被调用,比如 val n1 = Square(5)
  def apply(z: Double): Double = z * z
}

/**
  * @author 陈小哥cw
  * @date 2021/3/31 10:07
  */
object Test {
  def main(args: Array[String]): Unit = {
    val result = Square(6) // 36
    println(result)

    // 模式匹配使用:
    val number: Double = 36.0
    number match {
      case Square(n) => println(n) // 当对对象进行匹配时,会自动调用unapply方法
      case _ => println("nothing matched")// 当unapply返回None时,会匹配这里
    }
    
    
  }
}


运行结果

36.0
6.0

小结

  1. val result = Square(6),该语句在执行时,实际调用的是Square伴生对象中的
    apply方法,因此不用new关键字就能构造出相应的对象
  2. 若只提取对象的一个属性,则提取器为unapply(obj:Obj):Option[T]
  3. 若提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3…)]
  4. 若提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]
  5. case 中对象的unapply方法(提取器)返回Some,且所有属性均一致,才算匹配成功,
    属性不一致,或返回None,则匹配失败

2.5 匹配异常

package com.cw

import java.io.IOException

/**
  * @author 陈小哥cw
  * @date 2021/3/31 10:07
  */
object Test {
  def main(args: Array[String]): Unit = {
    try {
      println(3 / 0) // ArithmeticException
    } catch {
      case e: IOException => println("IOException")
      case e: ArithmeticException => println("算术运算异常[ArithmeticException]")
      case e: Exception => println("Exception")
    } finally {
      println("程序结束!")
    }

    println("---------------------------")

    var arr = List(1, 2, 3)
    try {
      println(arr(4)) // IndexOutOfBoundsException
    } catch {
      case e: IOException => println("IOException")
      case e: ArithmeticException => println("算术运算异常[ArithmeticException]")
      case e: Exception => println("Exception")
    } finally {
      println("程序结束!")
    }
  }
}


运行结果

算术运算异常[ArithmeticException]
程序结束!
---------------------------
Exception
程序结束!

三,变量声明和for表达式中的模式匹配

package com.cw.chapter11

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

    // TODO 变量声明中的模式匹配
    // match中每一个case都可以单独提取出来,意思是一样的.
    val (x, y) = (1, 2)
    println(x + " = " + y)

    val (username, age, email) = ("zhangsan", 20, "133@163.com")
    println(username + " = " + age + " = " + email)


    val (q, r) = BigInt(10) /% 3 // 包含了2个连续的运算符,可以同时获取结果和余数
    println("q = " + q) // 结果
    println("r = " + r) // 余数

    val arr = Array(1, 7, 2, 9)
    val Array(first, second, _*) = arr
    println(first, second)


    // TODO for表达式中的模式匹配

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

    for ((k, v) <- list) {
      println(k + " = " + v)
    }

    val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
    for ((k, v) <- map) {
      println(k + " -> " + v)
    }

    for ((k, 0) <- map) {
      println(k + " --> " + 0)
    }

    for ((k, v) <- map if v == 0) {
      println(k + " ---> " + v)
    }


  }
}

四,样例类

在scala中样例类是一种特殊的类,可用于模式匹配。

特点:

  • 构造器中的参数如果不被声明为 var 的话,它默认的话是 val 类型的,但一般不推荐将构造器中的参数声明为 var
  • 自动创建伴生对象,同时在里面给我们实现子 apply 方法,使得我们在使用的时候可以 不直接显示地 new 对象
  • 伴生对象中同样会帮我们实现 unapply 方法,从而可以将 case class 应用于模式匹配 apply 方法接受参数返回对象,unapply 方法接收对象返回参数
  • 实现自己的 toString、hashCode、copy、equals 方法
  • case class 主构造函数里面没有修饰符,默认的是 val

例1:

abstract class Notification

case class Email(sender: String, title: String, body: String) extends Notification

case class SMS(caller: String, message: String) extends Notification

case class VoiceRecording(contactName: String, link: String) extends Notification

Notification是一个虚基类,它有三个具体的子类Email, SMSVoiceRecording,我们可以在这些案例类(Case Class)上像这样使用模式匹配:

def showNotification(notification: Notification): String = {
  notification match {
    case Email(sender, title, _) =>
      s"You got an email from $sender with title: $title"
    case SMS(number, message) =>
      s"You got an SMS from $number! Message: $message"
    case VoiceRecording(name, link) =>
      s"you received a Voice Recording from $name! Click the link to hear it: $link"
  }
}
val someSms = SMS("12345", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")

println(showNotification(someSms))  // prints You got an SMS from 12345! Message: Are you there?

println(showNotification(someVoiceRecording))  // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123

showNotification函数接受一个抽象类Notification对象作为输入参数,然后匹配其具体类型。(也就是判断它是一个EmailSMS,还是VoiceRecording)。在case Email(sender, title, _)中,对象的sendertitle属性在返回值中被使用,而body属性则被忽略,故使用_代替。

例2

package com.cw.chapter11

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

    // 样例类 case class
    // TODO 基本介绍
    // 1) 样例类仍然是类
    // 2) 样例类用case关键字进行声明。
    // 3) 样例类是为模式匹配(对象)而优化的类
    // 4) 构造器中的每一个参数都成为val — —  除非它被显式地声明为var
    // 5) 在样例类对应的伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象
    // 6) 提供unapply方法让模式匹配可以工作
    // 7) 将自动生成toString、equals、hashCode和copy方法(有点类似模板类,直接给生成,供程序员使用)
    // 8) 除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们

    //val dollar = new Dollar(1.0)
    val dollar = Dollar(1.0) // TODO 伴生对象提供apply方法,不用new
    println(dollar.value)

    // TODO 当我们有一个类型为Amount的对象时,可以用模式匹配来匹配他的类型,并将属性值绑定到变量(即:把样例类对象的属性值提取到某个变量)
    for (amt <- Array(Dollar(1000.0), Currency(1000.0, "RMB"), NoAmount)) {
      val result = amt match {
        case Dollar(v) => "$" + v
        case Currency(v, u) => v + " " + u
        case NoAmount => ""
      }
      println(amt + ": " + result)
    }

    // TODO copy方法可以创建一个与现有对象值相同的新对象 ,并可以通过带名参数来修改某些属性
    val amt = Currency(29.95, "RMB")

    val amt1 = amt.copy()
    val amt2 = amt.copy(value = 19.95)
    val amt3 = amt.copy(unit = "英镑")

    println(amt)
    println(amt2)
    println(amt3)


    // 什么是中置表达式?1 + 2,这就是一个中置表达式。
    // 如果unapply方法产出一个元组,你可以在case语句中使用中置表示法。比如可以匹配一个List序列
    var list = List(1, 3, 5, 9)
    list match {
      case first :: second :: rest => println(first + " ** " + second + " ** " + rest)
      case _ => println("匹配不到...")
    }


  }
}

// TODO 密封类(sealed)介绍
// 1) 如果想让case类的所有子类都必须在申明该类的相同的源文件中定义,可以将样例类的通用超类声明为sealed,这个超类称之为密封类。
// 2) 密封就是不能在其他文件中定义子类。
// 当Amount没有声明sealed时,可以在其他文件中定义样例子类,声明后就只能在本文件声明子类了
// 所有的样例类最好在同一个文件中
abstract sealed class Amount

// 说明: 这里的 Dollar,Currencry, NoAmount  是样例类。
case class Dollar(value: Double) extends Amount

case class Currency(value: Double, unit: String) extends Amount

case object NoAmount extends Amount

特质(trait)和类(class)可以用sealed标记为密封的,这意味着其所有子类都必须与之定义在相同文件中,从而保证所有子类型都是已知的。

sealed abstract class Furniture
case class Couch() extends Furniture
case class Chair() extends Furniture

def findPlaceToSit(piece: Furniture): String = piece match {
  case a: Couch => "Lie on the couch"
  case b: Chair => "Sit on the chair"
}

这对于模式匹配很有用,因为我们不再需要一个匹配其他任意情况的case。

例3

object Test01 {
  def main(args: Array[String]): Unit = {
    val arr=Array(SubmitTask("1001","zs"),
      HeartBeat(10000),CheckTimeOutTask)
    arr(Random.nextInt(arr.length)) match {
      case SubmitTask(id,name) => println(id,name)  //这里能将样例类中的参数提取出来,是是因为有unapply方法
      case HeartBeat(time) => println(time)
      case CheckTimeOutTask => println("CheckTimeOutTask")
    }
  }
}

//多例样例类
case class SubmitTask(id: String, name: String)
//多例样例类
case class HeartBeat(time: Long)
//单例样例类
case object CheckTimeOutTask

例4:
在 Scala 中 Option 类型样例类用来表示可能存在或也可能不存在的值(Option 的子类有 Some 和 None)。Some 包装了某个值,None 表示没有值。

object Test01 {
  def main(args: Array[String]): Unit = {
    val map=Map("name"->"zs","age"->18,"address"->"beijing")
    val value=map.get("name")
    val result=value match {
      case Some(i) => i
      case None => 0
    }
    println(result)

    //上述方法可以用map的方法替代
    map.getOrElse("name",0)
  }
}

五,偏函数

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为 List[Int],而我们需要的是第一个元素是 0 的集合,这就是通过模式匹配实现的。

偏函数定义

val second: PartialFunction[List[Int], Option[Int]] = { 
	case x :: y :: _ => Some(y)
}

在这里插入图片描述
注:该偏函数的功能是返回输入的List 集合的第二个元素

偏函数原理
上述代码会被scala编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为Boolean

val second = new PartialFunction[List[Int], Option[Int]] {
	 //检查输入参数是否合格
	 override def isDefinedAt(list: List[Int]): Boolean = list match 
	 {
		 case x :: y :: _ => true
		 case _ => false
	 }
	 //执行函数逻辑
	 override def apply(list: List[Int]): Option[Int] = list match 
	 {
	 	case x :: y :: _ => Some(y)
	 } 
}

偏函数使用
偏函数不能像 second(List(1,2,3))这样直接使用,因为这样会直接调用 apply 方法,而应该调用 applyOrElse方法,如下

second.applyOrElse(List(1,2,3), (_: List[Int]) => None)

applyOrElse 方法的逻辑为if (ifDefinedAt(list)) apply(list) else default。如果输入参数满足条件,即 isDefinedAt返回true,则执行apply方法,否则执行defalut方法,default方法为参数不满足要求的处理逻辑。

案例实操
将该 List(1,2,3,4,5,6,“test”)中的 Int 类型的元素加一,并去掉字符串

package com.cw


/**
  * @author 陈小哥cw
  * @date 2021/3/31 14:28
  */
object Test {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, 5, 6, "test")
    val list1 = list.map {
      a =>
        a match {
          case i: Int => i + 1
          case s: String => s + 1
        }
    }
    println(list1.filter(a => a.isInstanceOf[Int]))
  }
}

方法一:
List(1,2,3,4,5,6,"test")
	.filter(_.isInstanceOf[Int])
	.map(_.asInstanceOf[Int] + 1)
	.foreach(println)
方法二:
List(1, 2, 3, 4, 5, 6, "test")
	.collect { 
		case x: Int => x + 1 
	}
	.foreach(println)

案例2

object Test01 {
  def main(args: Array[String]): Unit = {
    //普通方式的模式匹配
    val value = "aa"
    value match {
      case "aa" => println("0")
      case "bb" => println("1")
      case "aa" => println("0")
    }

    /**
      * 偏函数:
      * 语法:func1:PartialFunction[input,output]
      *   input:表示输入类型
      *   output:输出的类型
      *
      */
    def func1:PartialFunction[String,Int]={
      case "zs" =>18
      case "ls"=>18
      case "ww"=>20
      case _ =>100
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈小哥cw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值