scala学习笔记(十三) 模式匹配与样例类

时间:2021-12-27 07:32:02

如果要问scala的核心类容是什么,以我接触的情况来看,模式匹配与样例绝对是核心中的核心

 

因为scala是运行与JVM的,所以我们很多时候不自觉的会将他与java来进行比较,但是在这章之前它除了提供了一些让代码看起来更加简洁的语法糖外(语法糖大部分情况下可以被高级IDE弥补),似乎只有函数参数比较吸引人,而函数参数使用上面其实和C++的函数指针也是比较类似的,所以结论是,前面的都是scala的一些基础配件,而本章是核心

 

 

 

好了,下面进入本章的学习

 

本章要点:

1Match表达式类似于switch表达式,但是他进行了一些修改,比如:不在有break来结束分支,match中匹配的分支不会掉到下面的分支,使用 case _ (类似于java中的default)来处理没有被匹配的分支,否则会抛出MatchError

2、优先选择模式匹配而不是 isInstanceOf asInstanceOf

3

 

如果case关键字后面跟的是一个变量,则匹配的表达式会被赋值给这个变量,例如:

str(i)match {

   case '+' => …

   case '-' => …

  case ch => d =Character.digit(ch,10) //这里将str(i)赋值给了ch,后面是将ch传入函数

}

 

有一个特殊的情况是,常量的匹配,比如 case Pi => …   ,这里的Pi是一个常量,而不能被当成变量进行赋值,所以规则就是

模式匹配中的变量必须以小写字母开头,如果常量是以小写开头,则需要添加``进行逃生

 

匹配数组、列表和元组

 

要匹配数组的内容,可以在模式中使用Array表达式,像下面这样:

arr match{

   case Array(0) => "0"

   case Array(x,y) => x + " "+y

   case Array(0, _*) => "0 …"

   case _ => "something else"

}

第一个模式匹配包含0的数组。

第二个模式匹配任何带有两个元素的数组,并将值绑定到x y上面。

第三个模式匹配任何以0开始的数组。

 

如何需要被匹配的是列表,则应该这样

val b = lst match {

      case 0 :: Nil      => "0"

      case x :: y :: Nil => x + "" + y

      case 0 :: tail     => "0 …"

      case _             => "something else"

    }

这里有一个情况提一下,casex :: y ::Nil => x + " " + y,如果这里Nil没有加,则匹配出来是,x为列表第一个元素,y为列表的剩下部分(还是一个列表)

 

元组的匹配如下,很简单,不用过多解释,有一点要求是元素的个数需要对应

val pair = (0, 1)

    val c = pair match {

      case (0, _) => "0"

      case (x, y) => x + " " + y

      case (y, 0) => y + " 0"

      case _      => "something else"

    }

 

顺带提一下,match模式匹配是一个表达式,而非语句,所以是有返回值的。

 

模式匹配中也可以使用正则表达式。

 

模式匹配这一切内部的实现原理都是使用提取器,unapply unapplySeq。以后在我们自己的类想要按照我们自己的方式匹配,可以自定义提取器。

 

变量声明中的模式

val (x ,y) = (1 , 2)

这里一次定义了两个变量 x=1 y =2,这对于有些需要返回对偶的函数很有用,如果这并没有给你带来什么帮助,看看下面这样

val (q ,r) = BigInt(10) /% 3

这里q,r被自动匹配了

valArray(f, s, _*) = arr

这里提前arr的前两个值到f s


样例类

 

Scala中有一种样例类,和前面提到其他不同的事,样例类有几件事情是自动发生的:

 

1、构造器中的每个参数都是val,除非显示的定义为var

2、自动生成伴身类,并提供applyunapplycopy方法

 

密封类 sealed

由于样例类主要是用于模式匹配中,但是在匹配的case语句中,为了避免漏掉样例,可以将样例类的超类申明为密封类,这样做的结果是所以由这个超类派生的样例都必须定义在同一个文件中,并且编译器在编译器可以进行检查,并给出提示。

 

scala源码可以发现很多类别申明为密封类,在我有限的scala源码学习过程中,发现很多类将返回值包装成OptionOption类是一个样例类,并且支持泛型

 

比如Map类的get方法返回一个Option样例,如果有值则为Some(),否则为None

 

 

偏函数

被包含在花括号中的一组case语句被称作是偏函数 ---- 一个并非所有输入值都有定义的函数

,偏函数是PartialFunction[A,B]类的一个实例(A是参数类型,B是返回类型)


下面是一些练习

package demo

/**
 * @author Administrator
 *
 * 用于练习scala中的模式匹配
 *
 */
object ScalaMatch {

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

    val arr = Array(0, 3, 4, 2, 0)

    val a = arr match {
      case Array(0)     => "0"
      case Array(x, y)  => x + " " + y
      case Array(0, _*) => "0 …"
      case _            => "something else"
    }

    val lst = List(0, 1, 2, 3, 4)
    val b = lst match {
      case 0 :: Nil      => "0"
      case x :: y :: Nil => x + " " + y
      case 0 :: tail     => "0 …"
      case _             => "something else"
    }

    val pair = (0, 1)
    val c = pair match {
      case (0, _) => "0"
      case (x, y) => x + " " + y
      case (y, 0) => y + " 0"
      case _      => "something else"
    }

    val (x, y) = (1, 2)

    println(x + " " + y)

    val Array(f, s, _*) = arr

    println(f + " " + s)

    println(c)

    println(swap(12, 32))
    swap(Array(2, 3, 4, 5)) foreach println
    val ary = List(List(3, 8), 2, List(5))
    println(leafSum(ary))

    val leafTree = Node2(Node2(Leaf(3), Leaf(8)), Leaf(5))
    println(leafSum(leafTree))

    val nodeTree = Node('+', Node('*', Leaf(3), Leaf(8)), Leaf(2), Node('-', Leaf(5)))
    println(eval(nodeTree))
    
    val lo = List(Option(2),Option(4),None)
    println(sumOption(lo))
  }

  def leafSum(ary: List[Any]): Int = {
    var sum = 0
    for (a <- ary) {
      a match {
        case x: Int       => sum += x
        case x: List[Any] => sum += leafSum(x)
        case _            => 0
      }
    }
    sum
  }

  def leafSum(tree: BinaryTree): Int = {
    tree match {
      case node2: Node2 => leafSum(node2.left) + leafSum(node2.right)
      case leaf: Leaf   => leaf.value
      case node: Node   => node.subs.foldLeft(0)(_ + leafSum(_))
      case _            => 0
    }
  }

  def eval(tree: BinaryTree): Int = {
    tree match {
      case leaf: Leaf => leaf.value
      case node: Node =>
        node.ch match {
          case '+' => node.subs.foldLeft(0)(_ + eval(_))
          case '-' => node.subs.foldLeft(0)(_ - eval(_))
          case '*' => node.subs.foldLeft(1)(_ * eval(_))
          case '/' => node.subs.foldLeft(1)(_ / eval(_))
        }
    }
  }
  
  def sumOption(args : List[Option[Int]])={
    args.foldLeft(0)(_ + _.getOrElse(0))
  }

  def swap(t: Tuple2[Int, Int]) = {
    t match {
      case (x, y) => (y, x)
    }
  }

  def swap[T](array: Array[T]) = {
    array match {
      case Array(x, y, _*) =>
        array(0) = y; array(1) = x; array
      case _ => array
    }
  }

  abstract class Item

  case class Multiple(count: Int, item: Item, price: Double) extends Item

  sealed abstract class BinaryTree
  case class Leaf(value: Int) extends BinaryTree
  case class Node2(left: BinaryTree, right: BinaryTree) extends BinaryTree
  case class Node(ch: Char, subs: BinaryTree*) extends BinaryTree
}