Scala的控制语句

import java.io.Serializable

import scala.annotation.elidable
import scala.beans.BeanProperty
import scala.collection.immutable.HashMap
import scala.collection.mutable
import scala.reflect.ClassTag

/**
  * keyPoint
  * xx  08/07/25 09:00
  */
class ControlProgrammer extends FreeSpec {

  "控制语句" - {

    "while/do while使用" in {

      // while和do while没有返回值, 导致必须使用var变量, 在Scala函数式编程风格中建议使用递归代替while
      var sum = 0 // 必须使用var定义才能从while获取最后的值
      var x = 0
      while (x < 3) {
        sum += x
        x += 1
      }

      // do while
      var a = 0
      var b = 0
      do {
        a += b
        b += 1
      } while (b < 3)

      s"a: $a".show()
      s"sum: $sum".show()

    }

    "流间绑定: 避免多次计算, 绑定变量, 不需要val声明" in {

      val list = List("abc", 2, 3, "def", 5)
      for (
        elem <- list; // 遍历, 获取结果集
        str = elem.toString; // 指定遍历的结果集转String, 变量流间绑定到str
        s <- str // 遍历str
        if s > 'a' // str的结果集s, 大于'a'
      ) s"打印输出: $s".show() // 打印s

    }

    "try catch finally throw使用" in {

      def exception(x: Int, y: String): String = {
        // 对指定代码块内的内容进行try
        try {

          // 如果y=null, 出现NullPointerException
          y.equals("aaa")
          // 如果x<0, throw RunTime... x>0出现Arithmetic...
          if (x < 0) throw new RuntimeException else x / 0

          "try块执行成功!!!"

        } catch {
          // 异常打印并返回
          case e: NullPointerException => s"空指针异常: $e".show(); "第一个异常定义被捕获, 返回"

          case e: ArithmeticException => s"by zero异常: $e".show(); "第二个异常定义被捕获, 返回"

          case e: RuntimeException => s"运行时异常: $e".show(); "第三个异常定义被捕获, 返回"

        } finally {
          "finally必执行!".show();
          "第四个异常定义被捕获, 返回"
        }
      }

      exception(1, null).show() // 出现空指针, 捕获打印
      exception(1, "bbb").show() // 出现Arithmetic..., 捕获打印
      exception(-1, "bbb").show() // 出现Runtime..., 捕获打印

    }

    "抽取器:用来定义与对象表达解耦的模式(抽取器能截断数据表达和模式之间的联系, 这种属性被称为表征独立)" - {

      // TODO: apply和unapply在 对象/apply和unapply方法 in 中已经说明
      "抽取器就是具有名为unapply成员方法的对象。unapply方法的目的是为了匹配并分解值,apply是其对偶方法,unapply逆转了apply的构建过程,将构建的对象进行分解" in {
        class Email(val user: String, val domain: String)

        object Email {

          def apply(user: String, domain: String) = new Email(user, domain)

          // unapply方法反向变换,把一个字符串分解成两个部分:用户名和域名
          def unapply(email: Email): String = {
            val user = email.user
            val domain = email.domain
            user + "@" + domain
          }
        }

        Email.apply("lp", "242628")
        Email.unapply(new Email("lp", "242628")).show()

      }

    }

    /*
     * match表达式始终以值作为结果,这是Scala表达式的特点
     * Scala的备选项表达式永远不会意外掉入到下一个分支。在C或其他类C语言中,每个分支末尾要显式使用break语句来退出switch。
     * 如果没有模式匹配,MatchError异常会被抛出。这意味着你必须始终确信所有的情况都考虑到,或者至少意味着可以添加一个默认情况什么事都不做
     */
    "匹配模式match '数据结构的匹配'" - {

      "通配模式" in {

        def method[T](x: T): String = {
          x match {
            case 0 => "Zero"
            case 1 => "One"
            case List => "this is List"
            case _ => "Other" // 此处的_为通配模式的通配符. 意思为'其他', 类似于Java中switch的 default
          }
        }

        method(0).show() // Zero
        method(2).show() // 因为2没法匹配到具体的case, 所以被_通配符匹配, 打印: Other
        method(List).show() // this is List

        // TODO: 需要注意的是, match语句是根据case的顺序进行匹配的, 不要把通配_放在上面的case 比如:
        def method1(x: Int): String = {
          x match {
            case _ => "method1: Other"
            case 1 => "method1: One"
          }
        }

        method1(1).show() // 理论上1应该和case 1匹配, 返回method1: One, 但因为通配_放在case 1上面, 所以在上面的条件符合要求下, 直接返回了method1: Other

      }

      "常量模型" in {

        // 常量模式可以匹配和常量值本身相同的输入。任意的字面量都可以作为常量模式
        def method[T](x: T): String = {
          x match {
            case Set => "this is Set"
            case List => "this is List"
            case List(1, "2") => """this is List(1, "2")"""
            case 1 => "One"
            case _ => "All"
          }
        }

        method(1).show() // One
        method(List).show() // this is List
        method(List(1, "2")).show() // this is List(1, "2")
        method(Set("K" -> "V")).show() // All

      }

      "变量模式 类似于一种通配模式, 可以匹配任何对象, 不同的是把变量绑定在匹配的对象上" in {

        def method(x: Any): Any = {
          x match {
            case 0 => "Zero"
            case other => "此时此刻的云, " + other
          }
        }

        println(method(0))
        println(method("二十来岁的你")) // 被绑定在变量other上
        println(method(Set("二十来岁" -> "的你")))

      }

      "构造器模式: 提供了深度匹配,如果备选项是样本类,那么构造器模式首先检查对象是否为该备选项的样本类实例,然后检查对象的构造器参数是否符合额外提供的模式" - {

        "构造器模式" in {
          // 构造器模式不只检查顶层对象是否一致,还会检查对象的内容是否匹配内层的模式。由于额外的模式自身可以形成构造器模式,因此可以使用它们检查到对象内部的任意深度。

          //某个商店售卖物品,有时物品捆绑在一起打折出售
          abstract class Item
          case class Product(description: String, price: Double) extends Item
          case class Bundle(description: String, discount: Double, items: Item*) extends Item

          def price(it: Item): Double =
            it match {
              case Product(_, p) => p
              case Bundle(_, disc, its@_*) => its.map(price _).sum * (100 - disc) / 100
              //这里@表示将嵌套的值绑定到变量its
            }

          //测试
          val bun1 = Bundle("Father's day special", 20.0, Product("Massager", 188.0))
          val bun2 = Bundle("Appliances on sale", 10.0, Product("Haier Refrigerato", 3000.0), Product("Geli air conditionor", 2000.0))

          println(price(bun1))
          println(price(bun2))

        }

      }

      "序列模式" in {
        //序列模式,与构建序列的作用相反,用于对序列中的元素内容进行析取
        def patternMatching(x: AnyRef) = x match {

          //first,second 分别匹配序列中的第一、二个元素,_* 匹配序列中剩余其他元素
          case Array(first, second) => s"序列中第一个元素=$first,第二个元素=$second".show()

          // 匹配五个参数,进行提取打印
          case Array(one, two, three, four, five) => s"one: $one, two: $two, three: $three, four: $four, five: $five".show()

          case Array(first, _, three, _*) => s"序列中第一个元素=$first,第三个元素=$three".show()
          case _ => // 其他无视

        }

        patternMatching(Array(666, 999)) // 序列中第一个元素=666,第二个元素=999
        patternMatching(Array(1, 2, 3, 4, 5)) // one: 1, two: 2, three: 3, four: 4, five: 5
        patternMatching(Array(1, 2, 999, 4, 5, 6)) // 序列中第一个元素=1,第三个元素=999

      }

      "元组模式" in {

        def method(x: AnyRef) = {
          x match {
            case (1, 2, 3) => "(1, 2, 3)".show()
            case (a, b, c) => s"a: $a, b: $b, c: $c".show()
            case (a, b, c, _) => s"a: $a, b: $b, c: $c and ?".show()
            case _ => "all?".show()
          }
        }

        // 打印可看出, (1, 2, 3) 参数匹配到第一个case, 而(1, 2, 4)不满足第一个case, 匹配上了第二个case
        method((1, 2, 3)) // (1, 2, 3)
        method((1, 2, 4)) // a: 1, b: 2, c: 4
        method((1, 2, 3, 4)) // a: 1, b: 2, c: 3 and ?

      }

      "类型模式: 根据类型进行匹配" in {

        class A
        class B extends A
        class C extends A

        def method(x: Any) = {
          x match {
            case x: String => s"类型为: $x"
            case x: A => s"类型为: $x"
            case x: B => s"类型为: $x"
            case x: C => s"类型为: $x"
            case _ => s"类型为: $x"
          }
        }

        method(new A).show() // 类型为: com.dreamponline.scala_train.keypoint.ControlProgrammer$A$1@4082ba93
        method(new B).show() // 类型为: com.dreamponline.scala_train.keypoint.ControlProgrammer$B$1@609e8838
        method(new C).show() // 类型为: com.dreamponline.scala_train.keypoint.ControlProgrammer$C$1@43df23d3
        method("字符串").show() // 类型为: 字符串
        method(false).show() // 类型为: false

      }

    }

  }

}

 

展开阅读全文

没有更多推荐了,返回首页