美文网首页
Scala学习笔记 A2/L1篇 - 高阶函数 Higher-O

Scala学习笔记 A2/L1篇 - 高阶函数 Higher-O

作者: hakase_nano | 来源:发表于2018-08-13 19:34 被阅读0次

    教材:快学Scala

    chapter 12. 高阶函数 Higher-Order Functions

    12.1 functions as values

    • 方法(method)和函数(function)是不一样的,method不是值,function是值
    • method用def定义 function可以赋值给一个val
    • 方法转换成函数: val func = 方法 _ Scala无法直接操作(manipulate)方法,只能直接操作函数
    • 函数的用法:1.调用 2.传递(到变量/到另一个函数)

    12.2 匿名函数

    (x: Double) => 3 * x

    scala> val triple = (x: Double) => 3*x // 定义函数
    triple: Double => Double = <function1>
    
    scala> def triple(x: Double) = 3*x // 定义方法
    triple: (x: Double)Double
    
    • 函数参数可以不用圆括号()用花括号{}括住
      e.g.
      Array(3.14, 1.42, 2.0).map{ (x: Double) => 3 * x } 其中
      (x: Double) => 3 * x是函数map的参数!!

    12.3 函数作为参数

    • 作为参数的函数,它的类型写作 (参数类型) => 结果类型
    • 带函数参数的函数叫做高阶函数(higher-order function)

    12.4 参数类型推断 Parameter Inference

    • 在必要信息给出的前提下Scala可自动推断参数属于什么类型
    def quarter(f: (Double) => Double) = f(0.25) // 类型为((Double) => Double) => Double
    quarter((x: Double) => 3 * x)
    quarter((x) => 3 * x) // 从传入参数类型为(Double) => Double) 知道x的类型为Double 可省略
    quarter(x => 3 * x) // 只有一个参数(x) 可写成x
    quarter(3 * _) // 如果x在=>右侧只出现一次,可以用_代替x
    

    12.5 一些有用的高阶函数

    • 通用原则:如果你要的是一个序列的值,那么想办法从一个简单的序列转化得出
      (1 to 9).map("*" * _).foreach(println _)
      (1 to 9).filter(_ % 2 == 0) // 2,4,6,8
      (1 to 9).reduceLeft(_ * _) // 从左到右reduce (..((1 * 2) * 3) * ... * 9)
      (1 to 9).foldLeft(100)(_ + _) // 145 与reduceLeft区别是提供了迭代初始值
      "Mary has a little lamb".split(" ").sortWith(_.length < _.length)
      等价于
      "Mary has a little lamb".split(" ").sortWith((a: String, b: String) => a.length < b.length)

    12.6 闭包 Closures

    • A closure consists of code together with the definitions of any nonlocal variables that the code uses.
    • Java 8支持一种形式上受限的闭包

    12.7 SAM转换

    • 背景:Java不支持函数参数,需要将动作放进一个实现某接口的类中,再传递类的实例作为参数
    • 这种接口通常只有单个抽象方法(Single Abstract Method) Java中被称为SAM类型
    • 问题:如何实现函数参数 -> SAM参数的转换?
    • 答案:隐式转换(implicit)语法,通过自定义隐式转换实现需求
    implicit def makeAction(action: (ActionEvent) => Unit) = // action: 函数参数
    new ActionListener {                                     // ActionListener: 需要转换成的实例
        override def actionPerformed(event: ActionEvent) { action(event) }  // actionPerformed: SAM
    }
    // 效果:在所有预期ActionListener对象的地方传入任何(ActionEvent)=>Unit函数
    

    12.8 柯里化 Currying

    def mulOneAtATime(x: Int)(y: Int) = x * y

    • 用途:把多参数中的某个参数单独拎出来,提供用于类型推断的信息
    val a = Array("hello", "world")
    val b = Array("Hello", "World")
    a.corresponds(b)(_.equalsIgnoreCase(_))
    

    corresponds的定义为
    def corresponds[B](that: Seq[B])(p: (A, B) => Boolean): Boolean
    通过that参数知道B类型,用来推断函数p的输入类型

    12.9 控制抽象 Control Abstractions

    • 函数是"头等公民":一系列语句 -> 不带参数也没有返回值的函数
    • "不带参数也没有返回值的函数" 的函数类型:() => Unit
    def runInThread(block: () => Unit) { // 输入是一个过程(控制结构),表现为一个"不带参数也没有返回值的函数"block
        new Thread {
            override def run() { block() }
        }.start()
    }
    
    // 调用:{}内的是runInThread的参数,一个无参数无返回的函数
    runInThread { () => println("Hi"); Thread.sleep(1000); println("Bye") }
    

    想在调用中省略()=>写法,使用call-by-name(换名调用)

    def runInThread(block: => Unit) { // call-by-name语法:参数名:=>类型
        new Thread {
            override def run() { block } // block不加()
        }.start()
    }
    
    // 调用:不用加()=>
    runInThread { println("Hi"); Thread.sleep(1000); println("Bye") } 
    
    • call-by-name参数:函数被调用的时候,参数表达式不会被求值,而在函数体内每次出现都会求值一次
    • call-by-value参数:就是普通的参数,函数被调用的时候会被求值,然后函数体内每次出现都不会重新计算
    def until(condition: => Boolean)(block: => Unit) {
        if (!condition) {
            block
            until(condition)(block)
        }
    }
    
    var x = 10
    until (x == 0) {
        x -= 1
        println(x)
    }
    

    12.10 return表达式

    • return在Scala的作用:函数A的函数体内定义了匿名函数B,若B的函数体内使用return语句,则直接跳出A
    def indexOf(str: String, ch: Char): Int = {
        var i = 0
        until (i == str.length) {
            if (str(i) == ch) return i // 直接跳出indexOf
            i += 1
        }
        return -1
    }
    

    练习答案

    1. def values(fun: (Int) => Int, low: Int, high: Int) = (low to high) zip ((low to high).map(fun))
    2. reduceLeft(max(_,_))
    3. fibo(x: Int) = if(x != 0) (1 to x).reduceLeft(_*_) else 1
    4. fibo(x: Int) = (1 to x).foldLeft(1)(_*_)
    5. def largest(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.map(fun).reduceLeft(max(_,_))
    6. def largestAt(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.zip(inputs.map(fun)).reduceLeft((a, b) => if (a._2 > b._2) a else b )._1
    def adjustToPair[A, B, C](fun: (A, B) => C) = (pair: (A, B)) => fun(pair._1, pair._2) 
    (1 to 10).zip((11 to 20)).map(adjustToPair[Int, Int, Int](_+_))
    
    1. Array("i", "am", "cat").corresponds(Array(1, 2, 3))(_.length == _)
    def myCorresponds[A, B](iThis: Seq[A], iThat: Seq[B], p: (A, B) => Boolean): Boolean = {
        val pairs = iThis zip iThat
        // pairs.map(adjustToPair[A, B, Boolean](p)).filter(_ == false).length == 0
        // 看完13.8以后学到的新姿势
        pairs.map(adjustToPair[A, B, Boolean](p)).forall(_ == true)
    }
    
    1. def unless(condition: => Boolean)(block: => Unit) = if (!(condition)) { block }

    相关文章

      网友评论

          本文标题:Scala学习笔记 A2/L1篇 - 高阶函数 Higher-O

          本文链接:https://www.haomeiwen.com/subject/qhxpbftx.html