Scala学习笔记-3-函数

package day04

import java.io.{File, PrintWriter}

class Demo(desc: String) {

  override def toString: String = desc
}

object Demo {

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

    val d = new Demo("Scala 函数")
    println(d)

    test_1()

    test_2()

    test_3()

    test_4()

    test_5()

    // test_6()

    test_7()

    test_8()

    test_9()

    test_10()
  }

  // 类成员函数,和 Java 的成员方法一致,略

  /**
    * 局部函数:在函数内部定义函数
    * 在局部函数中可以访问外部定义的变量
    *
    */
  def test_1(): Unit = {
    val v_1 = "hello "

    def innerFun(): Unit = {
      println(v_1 + "inner function")
    }

    innerFun()
  }

  /**
    * 函数字面量(就是匿名函数)
    * 函数是 Scala 中的“头等公民”(W3C中这句话不知道什么意思。。。)
    * 匿名函数看起来不能指定返回值类型(语句最后一行执行的结果就是返回值)
    */
  def test_2(): Unit = {

    // 函数字面量
    var increase = (x: Int) => x + 1
    println(increase(6))

    // Lists 不可修改序列
    val someNumbers = List(-11, -10, -5, 0, 5, 10)

    // 在 foreach 中使用匿名函数
    someNumbers.foreach(i => println(i))

    // 在 filter 中使用匿名函数
    println(someNumbers.filter(i => i > 0))
  }

  /**
    * 部分应用的函数(其实就是 python 中的偏函数)
    */
  def test_3(): Unit = {

    // sum2 是 sum1 的简化写法
    // Scala 允许使用“占位符”下划线”_”来替代一个或多个参数,只要这个参数值函数定义中只出现一次,Scala编译器可以推断出参数。
    def sum1 = (a: Int, b: Int, c: Int) => a + b + c

    def sum2 = (_: Int) + (_: Int) + (_: Int)


    // 定义偏函数
    val b = sum2(1, _: Int, 3)

    println(b(10))
  }


  // 闭包
  def test_4(): Unit = {

    var more = 1

    // 定义:引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
    val addMore1 = (i: Int) => i + more
    println(addMore1(10))

    // 当这个自由变量发生变化时,Scala 的闭包能够捕获到这个变化,因此 Scala 的闭包捕获的是变量本身而不是当时变量的值。
    more = 100
    println(addMore1(10))

    // 如果变量在闭包在发生变化,也会反映到函数外面定义的闭包的值。
    val addMore2 = (i: Int) => {
      more = more + i
      more
    }
    println(addMore2(1000))
    println(addMore2(1000))
    println(more)
  }

  // 函数–可变参数,命名参数,缺省参数
  def test_5(): Unit = {

    // 可变参数
    def echo(args: String*): Unit = {
      args.foreach(s => println(s))
    }

    echo("1", "2", "3")
    val arr = Array("What's", "up", "doc?")
    echo(arr: _*) // 奇奇怪怪的语法

    // 命名参数:使用命名参数允许你使用任意顺序传入参数
    def speed(distance: Float, time: Float): Float = distance * time

    println(speed(10, 20))
    println(speed(time = 10, distance = 20))

    // 缺省参数值:Scala 在定义函数时,允许指定参数的缺省值,从而允许在调用函数时不指明该参数,此时该参数使用缺省值。缺省参数通常配合命名参数使用
    def printTime(out: java.io.PrintStream = Console.out, divisor: Int = 1) = out.println("time = " + System.currentTimeMillis() / divisor)

    printTime()
    printTime(divisor = 666)
  }

  /**
    * 尾递归:在最后一行调用自身的递归
    * 理论上来说,使用递归的性能不如使用循环
    * 但是 scala 的编译器会针对尾递归进行优化(优化为循环)
    */
  def test_6(): Unit = {
    def test(i: Int): Unit = {
      if (i > 100) {
        println("OVER")
        throw new Exception("boom!") // 用来显示调用栈
      } else {
        test(i + 1) // 只要最后一行不是调用自身,就不构成尾递归
      }
      // i + 1
    }

    test(0)
  }

  // 减低代码重复
  def test_7(): Unit = {

    // 函数可以作为参数传递(可以用来简化一些逻辑基本相似的代码)
    def check(s1: String, s2: String,
              checker: (String, String) => Boolean): Boolean = { // 该函数的类型为 (String,String ) =>Boolean,可以匹配任意使用两个 String 类型参数,返回值类型为 Boolean 的函数。
      checker(s1, s2)
    }

    def test(s1: String, s2: String): Boolean = {
      false
    }

    println(check("aaa", "aaa", _.equals(_)))
    println(check("aaa", "aaa", test(_, _)))
  }

  // 柯里化函数(没什么卵用):柯里化是把接受多个参数的函数变换成接受一个单一参数的函数
  def test_8(): Unit = {
    def test(i1: Int)(i2: Int)(i3: Int): Int = {
      i1 + i2 + i3
    }

    println(test(1)(2)(3))
  }

  // 创建新的控制结构:根据 scala 的高级函数特性,可以实现一些语法比较固定的控制结构
  def test_9(): Unit = {
    // 自定义控制结构
    def withPrintWriter(file: File, op: PrintWriter => Unit) {
      val writer = new PrintWriter(file)
      try {
        op(writer)
      } finally {
        writer.close()
      }
    }

    // 使用
    withPrintWriter(
      new File("date.txt"),
      writer => writer.println(new java.util.Date)
    )
  }

  // 传名参数
  def test_10(): Unit = {
    val assertionsEnabled = true

    // 传值参数
    def myAssert(predicate: () => Boolean) =
      if (assertionsEnabled && !predicate())
        throw new AssertionError

    // 传名参数
    def myNameAssert(predicate: => Boolean) =
      if (assertionsEnabled && !predicate)
        throw new AssertionError

    // 区别:传值参数是先计算后传值,传名参数相反
    myAssert(() => {
      // do something
      false
    })
    myNameAssert({
      // do something
      false
    })
  }
}
posted @ 2019-12-02 19:56  飞_2016  阅读(128)  评论(0编辑  收藏  举报