scala-higher order functions-closures-currying

  • Scala has mixed 面向对象和函数式features. In a functional programming language, functions are first-class citizens
    • We usually call expressions that can be passed as parameters to methods as functions
    • Functions that can receive functions as parameters or return functions are called higher-order functions
    • Higher-order functions include: functions as values, functions as parameters, anonymous functions, closures, currying, etc.

Function as value, as parameter, anonymous function-★★★★

  • Complete syntax:
    • val function name: (parameter type) => function return value type = (parameter name: parameter type) => function body
  • Shorthand syntax:
    • val function name = (parameter name: parameter type) => function body
  • Symbol interpretation
    • = Means assign the function on the right to the variable on the left
    • => The left side represents the input parameter name and type, the right side represents the function implementation and return value type
  • Anonymous function
    • A function that does not assign a function to a variable is called an anonymous function.
  • Method to function
    • val function name = method name _
package cn.hanjiaxiaozhi.basic3
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/13 16:24
 * Desc 演示证明Scala中的函数的本质是对象
 * 1.完整语法
 * val 函数名 :(参数类型)=>返回值类型 = (参数名称:参数类型)=>{函数体}
 * 2.简写语法
 * val 函数名 = (参数名称:参数类型)=>{函数体}
 */
object FunctionDemo2_Scala {
    
    
  def main(args: Array[String]): Unit = {
    
    
    //根据我们之前学习的编程语言,如果一个东西是对象的话吗,那么应该有如下的特征:
    //1.可以调用方法
    //2.可以作为值赋值给变量接收
    //3.可以当作参数被传递给方法
    //那么如果能演示出上面的三点不就证明了函数是对象嘛!!!//定义一些函数
    val f1 = (x: Int) => x
    val f2 = (x: Int, y: Int) => x + y
    //证明1.函数可以调用方法--推出-->函数是对象
    println(f1)//<function1>//直接打印函数,如果是对象的话,根据以前的经验,应该会调用对象的toString方法
    println(f2)//<function2>
    println(f1.toString())//<function1>//函数确实可以调用toString方法,那么如果和上面的打印结果一样,那么不就证明了我们的观点么!
    println(f2.toString())//<function2>//证明2.函数可以作为值赋值给变量接收--推出-->函数是对象
    val f3 = f2 //f2是上面定义好的函数,现在赋值给f3变量了, 那么f3变量也成了函数
    println(f3)//<function2>//证明3.函数可以当作参数被传递给方法--推出-->函数是对象
    val f4 = (x: Int, y: Int) => x + y
    //val result: Int = myMethod(1,2,f4) //f4是一个函数,现在被当作参数传递给了myMethod方法的fun参数了
    //也可以传递匿名函数
    val result: Int = myMethod(1,2,(x: Int, y: Int) => x + y) //f4是一个函数,现在被当作参数传递给了myMethod方法的fun参数了
    println(result)//3
    val result2: Int = f4(1,2)
    println(result2)//3}
  //定义一个方法,该方法接收2个int值,和1个函数,并在方法体中调用该函数,将2个int值传个该函数
  def myMethod(a:Int,b:Int,fun:(Int, Int) => Int):Int ={
    
    
    fun(a,b)//在方法体中调用函数,并将函数的计算结果作为myMethod方法的返回值
  }}
  • Review again
package cn.hanjiaxiaozhi.highfunction
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/19 9:52
 * Desc 演示高阶函数--其实就是函数的一些高级用法
 */
object FunctionDemo1 {
    
    
  def main(args: Array[String]): Unit = {
    
    
    //准备数据
    val list = List(1,2,3,4,5)
    //定义一个函数,将传入的参数扩大10倍
    val fun = (i:Int) => {
    
    i * 10}
    //1.函数作为值传递给另一个变量
    val f = fun
​
    //2.函数作为参数,传递给方法
    val res: List[Int] = list.map(f)
    println(res)//List(10, 20, 30, 40, 50)//3.匿名函数,就是将没有定义名称的函数直接传递给方法
    //val res2: List[Int] = list.map((i:Int) => {i * 10})//传入匿名函数
    //val res2: List[Int] = list.map(i => i * 10)//传入简写的匿名函数
    val res2: List[Int] = list.map(_*10)//传入简写的匿名函数
    println(res2)//4.定义一个方法并转换为函数然后进行传递
    def m(i:Int):Int={
    
    
      i*10
    }
    val f2 = m _ //方法转为函数
    val res3: List[Int] = list.map(f2)
    println(res3)//5.直接传递方法给函数型参数,编译器会自动将方法转为函数
    val res4: List[Int] = list.map(m)
    println(res4)
    
    //类似于
    //list.foreach(i=>println(i))
    //list.foreach(println _)
    list.foreach(println)
  }
}

Closure-★

Closures in Scala

  • What is a closure
    • A closure is actually a function, but the function depends on one or more variables declared outside the function
    • It is a function to include (close) external variables that do not belong to itself.
  • What will the closure cause
    • There may be multiple calls, and the results are inconsistent
  • How to solve the problem of closure
    • 1. Avoid closures (try not to use external variables in functions)
    • 2. Define variables and use val
package cn.hanjiaxiaozhi.highfunction
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/19 10:18
 * Desc 演示Scala函数的闭包
 */
object FunctionDemo2 {
    
    
  def main(args: Array[String]): Unit = {
    
    
    //定义一个变量,后续可以提供给函数使用
    var b = 10 //建议使用val修饰
    //定义一个函数,传入a并和函数外的变量b相加
    val add = (a: Int) => {
    
    
      a + b
    }//调用函数
    val res1: Int = add(1)
    println(res1) //11
    //修改b的值
    b = 100
    //再次调用函数,传入同样的值
    val res2: Int = add(1)
    println(res2)//101//观察上面的运行结果,我们会发现:
    //两次调用同一个函数add,传入通用的参数1,得到的结果尽然不同!
    //原因是因为函数add中用到了外部的不属于该add函数的变量b,形成了闭包现象
    //而b的值不受函数add的控制,在外部可能会发生改变,所以add函数的两次调用结果不一样!
    //问题如何解决?
    //1.尽量避免使用闭包(也就是尽量不要在函数内部使用不属于该函数的变量)
    //2.如果避免不了使用闭包,那么将用到的不属于该函数的变量用val修饰,如 val b = 10
    
    //总结:闭包就是函数内部使用了外部的变量,闭包可能会产生多次调用结果不一致的情况,可以通过避免使用闭包或使用val修饰变量来解决}
}
  • to sum up:
    • The closure is 函数内部使用了外部的变量,
    • Closure 可能会产生多次调用结果不一致的情况,
    • By avoiding the use of closures or val修饰变量to solve

Extension-Closures in Java-Just understand

package cn.hanjiaxiaozhi.highfunction;/**
 * Author hanjiaxiaozhi
 * Date 2020/7/19 10:29
 * Desc 演示Java中的闭包
 */
public class FunctionDemo_Java {
    
    
    //Scala中的闭包指的是函数用到了外部的变量
    //函数的本质是对象
    //所以Java中的闭包指的是匿名内部类对象中使用到了外部的变量!(因为Java中函数的本质就是匿名内部类对象)
    public static void main(String[] args) {
    
    
        /*final*/ int b = 10;
        int res1 = testAdd(1, new Fun() {
    
    
            @Override
            public int add(int a) {
    
    
                return a + b;
                //注意:匿名内部类对象中使用到了外部的变量,称作闭包
                //注意:Java中的闭包,会自动将使用到的外部变量加final修饰
            }
        });
        System.out.println(res1);//11
        //尝试修改b的值
        //b = 100;//这里不能对b就行修改,因为是闭包中使用的变量,默认已经加final了
        //testAdd(1,(int a)->{return a+b;});
        int res2 = testAdd(1, a -> a + b);
        System.out.println(res2);//11//总结:Java中的闭包,其实就是匿名内部类/函数中使用到了外部变量
        //但是在Java中会把在闭包中用到的外部变量自动加final修饰,所以不能修改,也就避免了一些问题}//定义一个方法,接收一个int a和一个Fun类型的对象,并在方法体中调用fun.add(a)
    public static int testAdd(int a, Fun fun) {
    
    
        return fun.add(a);
    }
}
​
interface Fun {
    
    
    int add(int a);
}

Currying

Insert picture description here
Insert picture description here

  • Introduce
  • A method/function in Scala can be called multiple times,每次只传递部分参数,返回一个函数,后面接着调用返回的函数传递其他的参数
  • In this way, some parameters can be bound conveniently, and the remaining parameters can be added later. This is actually the idea of ​​currying
  • Currying is used extensively in the source code of scala and spark. In order to facilitate the subsequent reading of the source code, we need to learn about currying.
  • What is Currying
    • Currying refers to the process of transforming a parameter list of a function/method that accepts multiple parameters into multiple parameter lists (each time a parameter is passed in, a new function will be returned to accept the remaining parameters)
    • For example: fun(a:Int,b:Int) becomes fun(a:Int)(b:Int)
  • The meaning of currying
    • https://www.zhihu.com/question/20037482
    • After the function is curried, only one parameter can be provided, and the other parameters are created as the "environment" of the function. This allows the function to return to its original state: a parameter goes in and a value comes out.
    • Currying can be called again 部分求值. A curried function receives some parameters. After receiving these parameters, the function does not evaluate immediately, but continues to return to another function. The parameters passed in just now are saved in the closure formed by the function, and wait until When the function really needs a value, all the parameters passed in before can be used for evaluation.
    • Use currying 可以简化主函数的复杂度,提高主函数的自闭性,使代码模块化,减少耦合增强其可维护性,提高功能上的可扩张性、灵活性。可以编写出更加抽象、功能化和高效的代码.
    • Currying is an inevitable result of the development of programming language thoughts with functions as the main body.
    • Currying is used in many places in Scala,如fold
  • The role of currying:可以对参数进行分批/归类传递
package cn.hanjiaxiaozhi.highfunction
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/19 10:18
 * Desc 演示Scala中的柯里化
 */
object FunctionDemo3 {
    
    
  def main(args: Array[String]): Unit = {
    
    
    println(add1(1, 2))//3
    println(add2(1)(2))//3//注意:柯里化的方法需要的多个参数可以分开传递
    //前几次传递会返回一个函数,直到最后一次传递返回方法计算的结果
    val tempFun: Int => Int = add2(1) //(b:Int) => 1 + b
    val res: Int = tempFun(2)
    //上面的分开调用和下面的等价
    val myTempFun:Int => Int = (b:Int) => 1 + b
    val res2: Int = myTempFun(2)
    
    println(res)//3
    println(res2)//3}
  //定义一个普通的方法,实现2个数相加
  def add1(a:Int,b:Int):Int={
    
    
    a + b
  }//定义一个柯里化的方法,实现2个数相加
  //柯里化指的是:将一次接收多个参数的方法转为了分多次接收
  def add2(a:Int)(b:Int):Int={
    
    
    a + b
  }
  //柯里化的作用:可以对参数进行分批/归类传递
  //如之前学习的 fold/foldLeft方法
  //fold(初始值)(函数)
  //val list = List[Int](1, 2, 3, 4, 5, 6, 7, 8, 9)//和为45
  //需求对list中的元素求和,并给定初始值
  //val res1: Int = list.fold(0)(_+_)
  //上面的fold就是典型的柯里化方式定义的方法
  //传递参数的时候,很容易进行区分,第一个是括号里的初始化值,第二个括号里的是函数
  //当然我们以后开发自己很少写这样的,都是源码中会偶尔定义这样的柯里化方法
  //我们调用的时候知道该方法是一个柯里化方法即可
}

Guess you like

Origin blog.csdn.net/qq_46893497/article/details/114044251