scala-pattern-match-basic

模式匹配概念

对数据进行深度分解和检查的一个功能,极其强大

涉及到两个术语:

  • extraction 提取
  • deconstruction 解构

在后面深入分析他俩

基础语法

类比Java中的swith

选择器 match {分支们}

每个分支就是一个模式,都是以case作为开头

我们要研究有哪些模式

模式类型

  1. 通配模式
  2. 常量模式
  3. 变量模式
  4. 构造方法模式
  5. 序列模式
  6. 元组模式
  7. 附带类型模式

通配模式

意思就是啥都能匹配

def myMatch(x:Any) = x match {
  case _ => println("通配")
}

对于这个方法,我们传入任何数据都能匹配到_ ,_是通配模式的符号。

myMatch(1)
myMatch("love")

这个模式选项一般放在最后,用来匹配其它选项

常量模式

所谓常量模式就是某个固定值直接匹配

def myMatch(x:Any) = x match {
  case 1 => println("数字1")
  case _ => println("其它")
}

变量模式

变量模式和通配有点相似,但是变量模式能完成数据的绑定,通配模式是不具有这个功能的

def myMatch(x:Any) = x match {
  case 1 => println("数字1")
  // v 是一个变量,并且绑定了值
  case v => println(v)
}

关于常量还是变量的问题有一些隐晦的知识稍微了解下

观察下面的代码

val number = 1

def myMatch(x:Any) = x match {
  case  number => println("数字1")
  // v 是一个变量,并且绑定了值
  case v => println(v)
}

看上去number就是1,应该是常量,但是实际上,Scala处理的时候将它视为了变量模式,和下面的v是一样的。

val Number = 1

def myMatch(x:Any) = x match {
  case  Number => println("数字1")
  // v 是一个变量,并且绑定了值
  case v => println(v)
}

上面的number首字母大写,scala处理的时候会去寻找该变量的真实值,将其视为常量,所以Scala用大小写区分了常量和变量模式。

另外还有集中情况也视为常量

import Math.PI
def myMatch(x:Any) = x match {
  case  PI => println("圆周率")
  // v 是一个变量,并且绑定了值
  case v => println(v)
}

这是从其它地方导入的值,放在这里也会视为常量

比如下面的写法也视为常量

object Demo:
  val girlName = "迪丽热巴"
end Demo
import Math.PI
def myMatch(x:Any) = x match {
  case  PI => println("圆周率")
  // 常量模式
  case Demo.girlName => println("漂亮不")
  case v => println(v)
}

某个类的实例引用其数据,也视为常量

class Girl(val name:String):
end Girl
val g = Girl("fbb")
def myMatch(x:Any) = x match {
 // 这也是常量
  case g.name => println("范冰冰")
  case v => println(v)
}

还有一种情况,可以把普通变量转为常量模式,借助反单引号也可以完成这个工作

val name = "fbb"
def myMatch(x:Any) = x match {
  case `name` => println("范冰冰")
  case v => println(v)
}

注意一点,scala区分的核心是写法,并非真实值

import Math.PI

val pi = PI
def myMatch(x:Any) = x match {
  case pi => println("圆周率???")
  case v => println(v)
}

这里使用了变量接收了PI,在case后使用了pi,此时其实它是变量模式

myMatch("fbb")
myMatch(Math.PI)

这里的测试都会进入pi case,因为Scala是按照形式来的,不是按照真实值来的。

小结

  • 导入的大小常量
  • 类级别实例持有数据
  • 反单引号包裹的变量
  • 大写开头的变量

上述几种情况都会视为常量模式

构造方法模式

case class Girl(val name:String,val age:Int):
  def this(name:String) = this(name,18)
end Girl
def myMatch(x:Any) = x match {
  case Girl(a,b) => println(s"$a,$b")
  case v => println(s"other $v")
}

Girl这里就是一种构造方法模式,

模式匹配能够解构任意深度,你还可以指定构造中某个数据必须是某个值,比如

def myMatch(x:Any) = x match {
  case Girl(a,18) => println(s"$a, 真18")
  case Girl(a,b) => println(s"$a,$b")
  case v => println(s"other $v")
}

第一个case会匹配到构造出Girl的,并且age必须为18,第二个就没有18的要求,对于更细化的case应该写在前面,否则无法匹配到。

序列模式

主要是List 这样的数据可以进行匹配

def myMatch(x:Any) = x match {
  case List(1,3,_) => println("1,3开头 共三个元素的列表")
  case v => println(s"other $v")
}
myMatch(List(1,3,2))
myMatch(List(1,3,4,5,6))

第一个match是三个元素而且1,3开头符合,第二个List长度不满足,所以只能匹配到v。

如果你想确定某几个元素,长度不限制需要使用_*写法,并且要在最后

def myMatch(x:Any) = x match {
  case List(1,_,3,_*) => println("1 中间随意,然后3,其余多少个元素无所谓")
  case v => println(s"other $v")
}
myMatch(List(1,3,3)) // 能匹配
myMatch(List(1,2,3,5,6)) // 能匹配
myMatch(List(3,5,6))// 第一个数据就不符合,无法匹配

元组模式

针对Tuple

def myMatch(x:Any) = x match {
  case (1,"love",_) => println("1111")
  case v => println(s"other $v")
}
myMatch((1,"love","haha")) // 能匹配
myMatch((2,"love","haha"))

元组中不要使用_*这样的写法,因为元组本身有长度限制和_*就不搭。

附加类型模式

对数据用数据类型描述,也可以匹配

def myMatch(x:Any) = x match {
  case a:String => println("字符串")
  case v => println(s"other $v")
}

有了类型,可以做更多的事情

def myMatch(x:Any) = x match {
  case a:String => println(a.length)
  case v => println(s"other $v")
}

这里a明确是String,所以可以使用length,a和x虽然是一个东西,由于类型描述不一样,你无法使用x.length,这一点要注意下

模式守卫

模式守卫就是在某个匹配模式后可以加入一些逻辑控制

def myMatch(x:Any) = x match {
  case a:String if a.length > 2  => println(s"$a 够长")
  case v => println(s"other $v")
}
myMatch("开心")// 长度不满足,匹配v
myMatch("开心吗") // 长度满足,匹配 a
myMatch((1,"love","haha"))

变量绑定

变量绑定是在某个模式中,将某个数据再次绑定到某个变量中,需要使用@符号,我们看一个案例

def myMatch(x:Any) = x match {
  case Girl(name, a  @ age) => println(a)
  case v => println(s"other $v")
}

这里的a绑定了后面的age,这个案例非常简单,以至于看不出它有啥用。

case class Girl(val name:String,val age:Int,val boyFriend: BoyFriend):
end Girl



case class BoyFriend(val name:String , val balance:Double):
end BoyFriend
def myMatch(x:Any) = x match {
  case Girl(name, age, b @ BoyFriend(_,_)) => println(b.name + b.balance)
  case v => println(s"other $v")
}

用这种模式不用在乎里面是什么数据,并且还能完成数据的引用,如果不用这种方式,像下面这样,我们想得到BoyFriend里面的数据怎么弄?

def myMatch(x:Any) = x match {
    case Girl(name, age, BoyFriend(_,_)) => println()
    case v => println(s"other $v")
}

还有一细致的点,在Scala编程中有一个案例,我们引用说明

abstract class Expr
case class Number(number: Double) extends Expr
case class Unop(oprator:String,arg:Expr) extends Expr
def myMatch(x:Any) = x match {
  case Unop("abs",e @ Unop("abs",_)) => e
  case _ =>
}
val r1 = myMatch(
  Unop("abs",
    Unop(
      "abs",
      Number(3.3)
    )
  )
)

println(r1)

打印

Unop(abs,Number(3.3))

这里的含义是,对一个数连续进行两个绝对值,这样写之后可以简化为只求一次绝对值

Unop("abs",
    Unop(
      "abs",
      Number(3.3)
    )
  )

本身可以简化为 Unop(“abs”,_) 而_部分又可以视为Unop("abs",_),这部分本身赋值给变量e了,所以后续的操作直接使用e本身做abs操作即可,不用走两次。

这段逻辑有点绕,需要一定的理解能力。

我们写一段伪代码来描述其逻辑,如果不是模式匹配,原始代码

val x = Unop("abs",
    Unop(
      "abs",
      Number(3.3)
    )
  )

进行求值的之后要

(x.arg.arg x.arg.operator) x.operator

经过模式匹配后由于直接得到了e,就是一个

Unop("abs", _)

所以如果利用它计算的话,就是属于

x.arg x.operator

我这里提供一个改写的可实验论证的代码,证明这一点

object Main {
  def main(args: Array[String]): Unit = {
    val x = Unop("abs",
      Unop(
        "abs",
        Number(3.3)
      )
    )

    // 这里要计算两次
    val v = x.cal();
    println(v)


    println("==============")
    val x2 = myMatch(x)
    val x3 = x2.asInstanceOf[Unop]
    // 这里只需要计算一次
    val v2 = x3.cal()
    println(v2)



  }


  def myMatch(x: Any):Any = x match {
    case Unop("abs", e @ Unop("abs", _)) => e
    case _ => None
  }


}


abstract class Expr:
  def cal():Any
end Expr

case class Number(number: Double) extends Expr:
  override def cal(): Any = number
end Number

case class Unop(oprator: String, arg: Expr) extends Expr:
  override def cal(): Any = oprator match {
    case "abs" => {
      println("计算绝对值")
      arg.cal()
    }
    case _ => None
  }
end Unop
 

元组增强功能

scala3对元组进行了增强,使其更像列表,但是保留了每个元素的类型,*:是一个符号,用于连接数据

val heros = Seq(
    ("傲之追猎者","雷恩加尔","狮子狗"),
    ("傲之追猎者","雷恩加尔","狮子狗","今晚猎个痛快"),
    ("海洋之灾","普朗克","船长"),
    ("武器大师","贾科斯","一灯"),
)


val result = heros.map{
    case "傲之追猎者" *: second *: third *:EmptyTuple => s"$third"
    case first *: other => s"$first : $other"
}
println(result)

结果

List(狮子狗, 傲之追猎者 : (雷恩加尔,狮子狗,今晚猎个痛快), 海洋之灾 : (普朗克,船长), 武器大师 : (贾科斯,一灯))

其中的EmptyTuple描述后面是空,所以第一个case就是三个元素限定死。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

singkingcho

有帮助?

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

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

打赏作者

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

抵扣说明:

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

余额充值