Scala学习整理[第七-九章 函数化编程的函数]<Programming In Scala>

第七章 内建控制结构

使用scala的语法糖和特性 ,改善代码结构 ,更少的var变量 /更简单的循环 /更多的嵌套筛选函数 .

//可以自定义包结构 ,还可以在同一文件中定义多个包
package SecondWithProgrammingInScala.Chapter7 {


  class ControlStructure(x: Int, y: Int) {
    var temp = x
    //if-else结构具有返回值 ,会将最后一行的变量返回
    println(if (x > y) x else y)

    //赋值语句不会再返回右边的值 ,不能使用 (x=2) < 3 这样的判断
    //println((temp = 2) < y) //error


    //for表达式进阶
    val args = Array("Hello", ",", "Scala", "!")

    //过滤(循环条件内使用if)
    for (arg <- args
         if arg != (",")
         if arg != ("!")
    ) print(arg)
    println()

    //嵌套
    for (arg <- args;
         c <- arg.toCharArray
    ) print(c)
    println()

    //制造新集合
    val newArgs =
    for (arg <- args
         if arg != (",")
         if arg != ("!")
    ) yield arg
    println(newArgs.toString)


    //try处理异常 throw抛出 catch捕捉 finally
    def tryCatch: String = {
      try {
        val a = x / y
        a.toString
      } catch {
        case ex: Exception => throw ex
      } finally {
        println("tryCatch方法结束")
      }
    }


    //match表达式 类似switch
    args.foreach(arg => {
      arg match {
        case "Hello" => print("欢迎")
        case _ => print(arg)
      }
    })
    println()


    //不使用break和continue ,用if-var(if true if false)去跳过和弹出
    //或者使用递归 (有一定风险) 需要优化
  }

  /**
    * 使用函数式风格打印一个乘法表
    * 和Java的区别主要在于语法和val的使用
    * 语法上 : 类的定义/隐藏的构造函数/if-else替代三元表达式/函数自带返回值
    * 风格上 : 减少了var的使用 ,将变量隐藏在函数参数中 ,所有值在进入函数体后都是不可改变的
    * 需要的每一个值都通过其他val值得出 ,层次比较清晰
    * 而var(Java中重复赋值) ,到最后可能都忘记之前是什么值做什么用
    */
  class MultiTable(row: Int, col: Int) {
    private val maxWidthOfNum = (row * col).toString.length

    private def makeRow(r: Int) = {
      for (c <- 1 to col) yield {
        val prod = (r * c).toString
        val width = maxWidthOfNum - prod.toString.length
        val padding = if (c == 1) " " * width else " " * (width + 2)
        padding + prod
      }
    }

    def printTable = {
      for (r <- 1 to row) println(makeRow(r).mkString + "\n")
    }
  }

  object Chapter7App {
    def main(args: Array[String]): Unit = {
      val multiTable = new MultiTable(12, 9)
      multiTable.printTable

      try {
        val test1 = new ControlStructure(6, 2)
        println(test1.tryCatch)

        val test2 = new ControlStructure(2, 0)
        println(test2.tryCatch)
      } catch {
        case ex: Exception => println(ex.getMessage)
      }
    }
  }
}

第八章 函数和闭包

package SecondWithProgrammingInScala

/**
  * 函数式风格会将函数划分一个一个小块 ,完成最简单最基本的功能 ,然后根据需要组装起来
  * 小的组件易于理解/修改 ,这也与<代码清洁之道>的思想相同
  * 只不过Java语言的设计上就是面向对象的 ,scala直接将函数作为主体 ,拆分起来更方便和简洁
  *
  * 因为主体是函数 ,scala设置了多种语法去描述/简化函数的定义和使用
  * 本地函数 : 函数中定义并使用 ,作用于只在父函数内(嵌套,类似变量)
  * 函数字面量 : (x:Int) => x+1 ,这是一个函数定义 ,但也可以将他作为变量保存 ,再调用变量
  * 占位符 : _ ,用来指代函数字面量中的参数 (只能指代一个 ,保持没有歧义)
  * 重复参数 : args:String* (类似java的 String ...args )
  */
object Fuction {
  //封闭函数
  def printTable(x: Int, y: Int) = {
    def printRow(x: Int) = {
      print("*" * x)
    }
    for (i <- 1 to y) {
      printRow(x)
      println()
    }
  }

  printTable(5, 5)
  //printRow() //error 封闭函数无法调用

  val array = Array(1, 2, 4, 5, 6)
  //函数字面量
  var p = (x: Int) => print(x)
  array.foreach(p)
  println()
  array.foreach((x: Int) => print(x))
  println()
  array.foreach(x => print(x))
  println()
  //占位符
  array.foreach(print(_))
  println()

  //重复参数
  def printWord(ch: String*): Unit = {
    println(ch.mkString(" "))
  }

  printWord("I'm", "a", "good", "Person")

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


/**
  * 闭包 : (x:Int) => x+more
  * more是一个自由变量 , 其值及类型是在运行的时候得以确定的
  * x是类型确定的 , 其值是在函数调用的时候被赋值的
  * x+more 是开放的(包含自由变量)open term
  * x+1 是封闭的closed term
  * 函数会在运行时捕获自由变量 ,完成从开放到封闭的过程 -> 闭包
  */
object Closure {
  def main(args: Array[String]): Unit = {
    //1闭包会在使用时去捕捉自由变量的值 ,形成封闭的函数再进行调用
    var more = 0
    val addMore = (x: Int) => x + more //x+1

    more = 1 //x+1
    println(addMore(10)) //11
    more = 99 //x+99
    println(addMore(10)) //109

    //2闭包内的变化也反馈到外部
    val someNumbers = List(-11, -10, -5, 0, 5, 10)
    var sum = 0
    someNumbers.foreach(sum += _)
    println(sum)

    //3如果需要多个版本的more共存 ,并分别调用
    //对每一个版本的more ,生成不同的addMore函数(快照)
    //调用闭包时 ,依赖的外部变量已经确定( = 定义闭包时的more变量)
    def makeIncreaser(more: Int) = (x: Int) => x + more

    val func1 = makeIncreaser(1) // more=1
    val func99 = makeIncreaser(99) // more=99

    println(func1(10)) //11
    println(func99(10)) //109
  }
}

/**
  * 尾递归
  * 递归方法比循环更容易理解 ,代码也更加清晰
  * 但是实际情况中 ,递归重复调用函数 ,导致方法栈溢出是一个严重的问题
  * 因此scala对递归进行优化 ,让你书写递归并转换为高效的循环
  * 但这个优化仅支持严格的尾递归
  */
object Chapter8App {

  //最后一个是+操作
  def boom(x: Int): Int = {
    if (x == 0) throw new Exception("boom!") else boom(x - 1) + 1
  }

  //最后一个是递归
  def bang(x: Int): Int = {
    if (x == 0) throw new Exception("boom!") else bang(x - 1)
  }

  def main(args: Array[String]): Unit = {
    //只调用一次bang函数 异常在里面
    bang(5)

    //调用多次boom函数
    boom(5)
  }
}

第九章 控制抽象

package SecondWithProgrammingInScala

import java.io.{File, PrintWriter}

/**
  * 闭包的高级应用
  *
  * 参考知乎对JavaScript的闭包研究
  * [到底什么是闭包 - 张恂老师](https://zhuanlan.zhihu.com/p/21346046)
  *
  * 函数A中声明函数B ,函数B作为参数传递给函数C并在C中执行 ,因为可以访问到A中的变量 ,称B的上下文是闭包
  *
  * 闭包是一个有状态的函数/一个有记忆的函数/一个只有一个方法的对象
  * 传统函数是没有状态的 ,变量必须放在函数参数中传递 ,每次调用是幂等的
  * 闭包则是把函数作为保存变量的环境 ,闭包内的变量被放在了不同的地方
  * (函数内的局部变量在栈上 函数退出即销毁 ;闭包内的数据被放在堆上 可以重复访问)
  *
  * 闭包的出现是因为词法作用域(lexical scope) ,在函数作为头等公民的语言(scala,Javascript)中 ,函数可以作为参数和返回值
  * 如果没有闭包 ,那么函数的free变量(不在使用处定义的变量)就没法正确的引用
  */
object ClosureApplication {
  private def filesHere = (new java.io.File(".")).listFiles

  //函数简化聚合 : 可以在函数内定义一个函数结构 ,将函数作为参数传递进去 类似与同一接口不同实
  private def filesMatching(matcher: (String) => Boolean) = {
    for (file <- filesHere; //函数作为变量
         if matcher(file.getName)) //调用定义的函数->实际传入的函数
      yield file
  }

  /**
    * 这里使用了闭包的概念 - filesEnding函数A - _.endsWith(query)函数B - filesMatching函数C
    * 调用filesEnding(".scala")时 ,".scala"作为一个自由变量(函数A的参数) ,被函数字面量_.endsWith(query)捕获
    * 产生了新的函数_.endsWith(".scala") (引用函数A的参数的函数B),并将这个函数传入filesMatching(函数C)
    * 此时传递的是已经闭合的函数快照(函数C中使用函数B ,函数B已经捕获函数A的参数)
    */

  def filesEnding(query: String) =
  filesMatching(_.endsWith(query))

  def filesContaining(query: String) =
    filesMatching(_.contains(query))

  def filesRegex(query: String) =
    filesMatching(_.matches(query))

  filesEnding("txt")

  //把函数作为变量可以创建复杂的控制结构(对函数进行操作)
  //当一段逻辑需要重复多次 就可以进行重写 将变化的部分写成函数参数(有点像java的多态)
  //定义一个函数 将传入的函数递归一次
  def twice(op: Double => Double, x: Double) = op(op(x))

  val three = (1 + 1) + 1
  val four = twice(_ + 1, 2)
  println(three)
  print(four)

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

/**
  * 柯里化
  * 把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数
  * 类似于之前的闭包 将原函数拆分成2级 传入第一个参数生成函数快照 用快照接受剩下的函数进行运行
  */
object Currying {
  def curriedSum(x: Int)(y: Int) = x + y

  def closureSum(x: Int) = (y: Int) => x + y

  val a = curriedSum(1)(2)
  val b = closureSum(1)(2)
  //因为参数列表分离 可以用Currying形成快照
  //但原函数有2个参数列表 ,第二个参数用_标识 ,不可省略(与闭包快照不同)
  val onePlus = curriedSum(1) _
  val twoPlus = curriedSum(2) _
  //调用函数快照
  val result = onePlus(2) + twoPlus(3)

  println(a)
  println(b)
  println(onePlus)
  println(twoPlus)
  println(result)

  //租赁模式(柯里化+函数参数) : 比如对文件操作结束必须要关闭 ,将这个操作屏蔽在内部 ,把正常功能租给外界使用(有点像AOP)
  def withPrintWriter(file: File)(op: PrintWriter => Unit) {
    val writer = new PrintWriter(file)
    try {
      op(writer) //调用用户传入的方法 并将writer作为参数传入
    } finally {
      writer.close()
    }
  }

  //(){}式传参 : 对只有一个参数的函数 (){}都可以传递参数 通过柯里化应用到多个参数上
  val file = new File("test.txt")
  //对于柯里化函数的每一个参数 ,都能生成 只有一个参数的函数(半成品) ,因此都可以使用{}
  withPrintWriter(file) {
    writer => writer.println("file")
  }
  withPrintWriter {
    file
  } { writer => writer.println("file") }

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

/**
  * 传名参数
  * 传值参数
  */
object ByNameParameter {

  //传名参数 : 传递函数类型的参数 并且省略参数列表
  def method1(predicate: => Boolean) =
  print("传名参数")

  //传值参数 : 传递基本类型的参数
  def method2(predicate: Boolean) =
  print("传值参数")

  //使用传名参数+柯里化 可以形成原生if else的效果
  def if2(assertion: => Boolean)(method: => Any) {
    if (assertion) method //如果判断条件成立 则执行条件体内的方法
    else println("不成立")
  }

  def main(args: Array[String]): Unit = {
    if2(3 > 2) {
      println("成立")
    }
    if2(2 > 3) {
      println("成立")
    }
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值