Scala-函数的高阶使用

函数高级

高阶函数

在 Scala 中,函数是一等公民,对于一个函数我们可以:定义函数、调用函数

高阶运用:

1、函数可以作为值进行传递

object TestFunction {

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

        def f(n: Int): Int = {
            println("f被调用啦")
            n + 1
        }

        val result = f(123)
        println(result) // 124

        val f1: Int => Int = f
        val f2 = f _
        println(f1)      // 函数的引用地址
        println(f1(100)) // 101
        println(f2(200)) // 201
        
        
        def fun(): Int = {
            println("fun被调用啦")
            1
        }
        
        val f3 = fun _
        val f4: () => Int = fun
    }
}

2、函数可以作为参数进行传递

object TestFunction {

    def main(args: Array[String]): Unit = {
        // 定义二元计算函数
        def dualEval(op: (Int, Int) => Int, a: Int, b: Int): Int = {
            op(a, b)
        }

        def add (a: Int, b: Int) : Int = {
            a + b
        }

        dualEval(add, 12, 15)
        dualEval((a, b) => a + b, 12, 15)
    }
}

3、函数可以作为函数返回值返回

object TestFunction {

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

        def f5(): Int => Unit = {
            def f6 (a: Int): Unit = {
                println("f6备调用啦" + a)
            }
            f6 // 将函数直接返回
        }

        val f6 = f5()
        println(f6) // 得到的是f6的引用
        println(f6(123)) // f6备调用啦123
        println(f5()(25)) // f6备调用啦25
    }
}

实例

object TestPractice {

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

        // 定义一个匿名函数,并将它作为值赋给变量 fun。函数有三个参数,类型分别为 Int,String,Char,返回值类型为 Boolean。
        // 要求调用函数 fun(0, “”, ‘0’)得到返回值为 false,其它情况均返回 true。
        val fun = (i: Int, s: String, c: Char) => { if (i == 0 && s == "" && c == '0') false else true }

        println(fun(0, "", '0'))  // false
        println(fun(1, "", '0'))  // true
        println(fun(0, "1", '0')) // true
        println(fun(0, "", '1'))  // true
        println(fun(1, "1", '1')) // true


        // 定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。
        // 它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。
        // 函数 f2 接收一个 Char 类型的参数,返回一个 Boolean 的值。
        // 要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true。
        def func(i: Int): String => (Char => Boolean) = {
            def f1(s: String): Char => Boolean = {
                def f2(c: Char): Boolean = {
                    if (i == 0 && s == "" && c == '0') false else true
                }
                f2
            }
            f1
        }
        println(func(0)("")('0'))  // false
        println(func(1)("")('0'))  // true
        println(func(0)("1")('0')) // true
        println(func(0)("")('1'))  // true
        println(func(1)("1")('1')) // true
        
        // 匿名函数的简写
        def func1(i: Int): String => (Char => Boolean) = {
            s => c => if (i == 0 && s == "" && c == '0') false else true
        }

        // 柯里化
        def func2(i: Int)(s: String)(c: Char): Boolean = {
            if (i == 0 && s == "" && c == '0') false else true
        }
    }
}

匿名函数

没有名字的函数就是匿名函数。

(x: Int) => {函数体}

x:表示输入参数类型;Int:表示输入参数类型;函数体:表示具体代码逻辑

传递匿名函数至简原则:

(1)参数的类型可以省略,会根据形参进行自动的推导

(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。

(3)匿名函数如果只有一行,则大括号也可以省略

(4)如果参数只出现一次,则参数省略且后面参数可以用_代替

object TestLambda {

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

        val fun = (name: String) => { println(name) }
        fun("test") // test

        // 定义一个函数,以函数作为参数输入
        def f(func: String => Unit): Unit = {
            func("testF")
        }

        f(fun) // testF
        f((name: String) => { println(s"---$name") }) // ---testF

        // (1)参数的类型可以省略,会根据形参进行自动的推导
        f((name) => { println(s"---$name") })

        // (2)类型省略之后,发现只有一个参数,则圆括号可以省略; 其他情况:没有参数和参数超过1的永远不能省略圆括号。
        f(name => { println(s"---$name") })

        // (3)匿名函数如果只有一行,则大括号也可以省略
        f(name => println(s"---$name"))

        // (4)如果参数只出现一次,则参数省略且后面参数可以用_代替
        f(println(_))

        // (5)如果可以推断出,当前传入的println是一个函数体,而不是调用语句,可以直接省略下划线
        f(println)
        
        // 实例,定义一个二元运算函数,只操作1和2,但是具体的运算通过参数传入
        def dualFunctionOneAndTwo(fun: (Int, Int) => Int): Unit = {
            fun(1, 2)
        }

        val add = (a: Int, b:Int) => a + b
        val minus = (a: Int, b:Int) => a - b

        dualFunctionOneAndTwo(add)
        dualFunctionOneAndTwo(minus)

        // 简化:
        dualFunctionOneAndTwo((a: Int, b: Int) => a + b)
        dualFunctionOneAndTwo((a, b) => a + b)
        dualFunctionOneAndTwo(_ + _)
    }
}

高阶函数案例

Map映射

object TestPracticeCollection {

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

        val arr: Array[Int] = Array(1, 11, 111, 1111)

        // 对数组进行处理,讲操作抽象出来,处理完毕的结果返回一个新的数组
        def arrayOperation(array: Array[Int], op: Int => Int): Array[Int] = {
            for (elem <- array) yield op(elem)
        }

        // 定义一个加1操作
        def addOne(a : Int): Int = {
            a + 1
        }

        // 调用函数
        val newArray = arrayOperation(arr, addOne)
        println(newArray.mkString(",")) // 2,12,112,1112

        // 调用匿名函数
        val newArray2 = arrayOperation(arr, (a) => a * 2)
        println(newArray2.mkString(",")) // 2,22,222,2222
    }
}

函数柯里化&闭包

闭包:函数式编程的标配

闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包

函数柯里化:把一个参数列表的多个参数,变成多个参数列表。

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

        def add(a: Int, b: Int): Int = {
            a + b
        }

        // 1.考虑固定一个加数的场景
        def addByFour(b: Int): Int = {
            4 + b
        }

        // 2.拓展固定加数改变的情况
        def addFive(b: Int): Int = {
            5 + b
        }

        // 3.将固定加数作为另一个参数传入,但是是作为“第一层参数”传入
        def addByFour1(): Int => Int = {
            def addB(b: Int): Int = {
                4 + b
            }

            addB
        }

        def addByA(a: Int): Int => Int = {
            def addB(b: Int): Int = {
                a + b
            }

            addB
        }

        val addByFour2 = addByA(4)
        val addByFive2 = addByA(5)
        println(addByFour2(15)) // 19
        println(addByFive2(10)) // 15

        // 4.addByA 的Lambda表达式
        def addByA1(a: Int): Int => Int = {
            (b: Int) => {
                a + b
            }
        }
        def addByA2(a: Int): Int => Int = {
            b => a + b
        }
        def addByA3(a: Int): Int => Int = a + _

        val addByFour3 = addByA(4)
        val addByFive3 = addByA(5)
        println(addByFour3(15)) // 19
        println(addByFive3(10)) // 15

        // 5.柯里化
        def addByCurrying(a: Int)(b: Int):Int = { a + b }
        println(addByCurrying(12)(13)) // 25
    }
}

递归

一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用,这样的函数就叫做递归函数

递归算法

  1. 方法调用自身

  2. 方法必须要有跳出的逻辑

  3. 方法调用自身时,传递的参数应该有规律

  4. scala 中的递归必须声明函数返回值类型

object TestRecursion {
    def main(args: Array[String]): Unit = {
        println(fact(5)) // 120
    }

    // 递归实现阶乘
    def fact(i: Int): Int = {
        if (i == 0) {
            // 0! = 1
            // 只有最后一行代码的return可以省略
            return 1
        }
        fact(i - 1) * i
    }

    // 尾递归实现
    def tailFact(n: Int): Int = {
        // 这个注解当方法不是尾递归方法时会报错
        @tailrec
        def loop(n: Int, result: Int): Int = {
            if (n == 0) return result
            loop(n - 1, result * n)
        }

        loop(n, 1)
    }
}

递归会耗费更多的栈空间资源,有可能会导致栈溢出。尾递归的话,每次递归会将上次递归调用弹出,并将本次调用压入。

控制抽象

1、值调用(传值参数):把计算后的值传递过去

2、名调用(传名参数):把代码传递过去

Java 只有值调用;Scala 既有值调用,又有名调用

object TestControlAbstraction {

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

        // 1.传值参数
        def f0 (a: Int): Unit = {
            println("a: " + a)
        }

        f0(123) // a: 123

        def f1(): Int = {
            println("f1被调用")
            12
        }
        f0(f1()) // a: 12

        // 2.传名参数,参数不再是具体的值,而是代码块,这里的代码块的返回值是Int
        def f2 (b: => Int): Unit = {
            println("b: " + b)
            println("b: " + b)
        }

        f2(23)
        // f1将会被调用两次,因为这里我们把b就直接当前成了f1()来使用了,因此名传递是将代码块直接传递进去
        // f1被调用
        // b: 12
        // f1被调用
        // b: 12
        f2(f1())
        // 代码块会被调用两次,并且返回值是129
        // 这里是一个代码块
        // b: 129
        // 这里是一个代码块
        // b: 129
        f2({
            println("这里是一个代码块")
            129
        })
    }
}

自定义一个while循环

object TestMyWhile {

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

        // 1.常规的while循环
        while (n >= 1) {
            println(n)
            n -= 1
        }

        // 2.用闭包实现一个函数,将代码块作为参数传入,递归调用
        def myWhile(condition: => Boolean): (=> Unit) => Unit = {
            // 内层函数需要递归调用,参数就是循环体
            def doLoop(op: => Unit): Unit = {
                if (condition) {
                    op
                    myWhile(condition)(op)
                }
            }
            doLoop _
        }

        n = 10
        myWhile(n >= 1) {
            println(n)
            n -= 1
        }

        // 3.用匿名函数实现
        def myWhile2(condition: => Boolean): (=> Unit) => Unit = {
            // 内层函数需要递归调用,参数就是循环体
            op => {
                if (condition) {
                    op
                    myWhile2(condition)(op)
                }
            }
        }

        n = 10
        myWhile2(n >= 1) {
            println(n)
            n -= 1
        }

        // 4,用柯里化实现
        def myWhile3(condition: => Boolean)(op: => Unit):Unit = {
            // 内层函数需要递归调用,参数就是循环体
            if (condition) {
                op
                myWhile3(condition)(op)
            }
        }

        n = 10
        myWhile3(n >= 1) {
            println(n)
            n -= 1
        }
    }
}

惰性加载

当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。这种加载方式称之为惰性加载

lazy 不能修饰 var 类型的变量

object TestLazy {

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

        lazy val result: Int = sum(13, 47)
        println("1.函数调用")
        println("2.result = " + result)
        println("4.result = " + result)

        def sum(a: Int, b: Int): Int = {
            println("3. sum被调用了")
            a + b
        }
    }
}

// 结果如下:
1.函数调用
3. sum被调用了
2.result = 60
4.result = 60
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值