Scala入门(五)函数式编程

总目录:https://blog.csdn.net/treesorshining/article/details/124697102

总目录


1)面向对象编程

解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。

对象:用户

行为:登录、连接 JDBC、读取数据库

属性:用户名、密码

Scala 语言是一个完全面向对象编程语言,万物皆对象。

对象的本质:对数据和行为的一个封装

2)函数式编程

解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。

扫描二维码关注公众号,回复: 15674434 查看本文章

例如:请求->用户名、密码->连接 JDBC->读取数据库

Scala 语言是一个完全函数式编程语言,万物皆函数。

函数的本质:函数可以当做一个值进行传递

3)在 Scala 中函数式编程和面向对象编程完美融合在一起了。

1.函数基础

1.1 函数基本语法

1)基本语法
在这里插入图片描述
2)案例

// 定义一个函数,实现打印传入的名称
// (1)函数定义
def f(arg: String): Unit = {
    
    
    println(arg)
}
// (2)函数调用
// 函数名(参数)
f("hello world")

1.2 函数和方法的区别

1)核心概念

(1)为完成某一功能的程序语句的集合,称为函数。

(2)类中的函数称之方法。

注意:(1)Scala 语言可以在任何的语法结构中声明任何的语法

(2)函数没有重载和重写的概念;方法可以进行重载和重写

(3)Scala 中函数可以嵌套定义

2)案例

object Test_function {
    
    
    def main(args: Array[String]): Unit = {
    
    
        def test(arg: String): Unit = {
    
    
            println(arg)
        }

        test("test")	// test(注:如果上方test函数不存在,则会执行main函数外的那个test方法)

        Test_function.test("test")	// Test
    }

    def test(arg: String): Unit = {
    
    
        println(arg.toUpperCase)
    }
}
object Test_function {
    
    
    // 方法可以进行重载和重写,程序可以执行
    def main(): Unit = {
    
    
    }

    def main(args: Array[String]): Unit = {
    
    
        // (1)Scala 语言可以在任何的语法结构中声明任何的语法
        import java.util.Date
        new Date()
        // (2)函数没有重载和重写的概念,此处会报错
        def test(): Unit ={
    
    
            println("无参,无返回值")
        }
        test()
        def test(name:String):Unit={
    
    
            println()
        }
        //(3)Scala 中函数可以嵌套定义
        def test2(): Unit ={
    
    
            def test3(name:String):Unit={
    
    
                println("test")
            }
        }
    }
}

1.3 函数定义

1)函数定义

(1)函数 1:无参,无返回值

(2)函数 2:无参,有返回值

(3)函数 3:有参,无返回值

(4)函数 4:有参,有返回值

(5)函数 5:多参,无返回值

(6)函数 6:多参,有返回值

2)案例

// 函数 1:无参,无返回值
def test1(): Unit = {
    
    
    println("无参,无返回值")
}
test1()
// 函数 2:无参,有返回值
def test2():String = {
    
    
    return "无参,有返回值"
}
println(test2())
// 函数 3:有参,无返回值
def test3(s:String):Unit = {
    
    
    println(s)
}
test3("test")
// 函数 4:有参,有返回值
def test4(s:String):String = {
    
    
    return s+"有参,有返回值"
}
println(test4("test"))
// 函数 5:多参,无返回值
def test5(name:String, age:Int):Unit = {
    
    
    println(s"$name, $age")
}
test5("test",40)
// 函数 6:多参,有返回值
def test5(name:String, age:Int):Unit = {
    
    
    return s"$name, $age"
}
println(test6("test",40))

1.4 函数参数

1)案例

(1)可变参数

(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后

(3)参数默认值,一般将有默认值的参数放置在参数列表的后面

(4)带名参数

// (1)可变参数
def test(s: String*): Unit = {
    
    
    println(s)
}
// 有输入参数:输出 WrappedArray
test("Hello", "Scala")	// WrappedArray(Hello, Scala)
// 无输入参数:输出 List()
test()	// List()
// (2)如果参数列表中存在多个参数,那么可变参数一般放置在最后
def test2(name : String, s: String*): Unit = {
    
    
    println(name + "," + s)
}
// 即使只传入一个参数,可变参数也有对应的值
test2("test1")	// test1,WrappedArray()
test2("test1", "test2")	// test1,WrappedArray(test2)
// (3)参数默认值
def test3(name : String, age : Int = 30): Unit = {
    
    
    println(s"$name, $age")
}
// 如果参数传递了值,那么会覆盖默认值
test3("test", 20)	// test, 20
// 如果参数有默认值,在调用的时候,可以省略这个参数
test3("test")	// test, 30
// 一般情况下,将有默认值的参数放置在参数列表的后面
// Scala 函数中参数传递是,从左到右
// 类似于以下情况,将有默认值的参数放置在参数列表的前面则没有任何意义
// 因为必须要传入两个参数,默认值必然被覆盖,除非使用带名参数
def test4(sex : String = "男", name : String): Unit = {
    
    
    println(s"$name, $sex")
}
//(4)带名参数
// 在存在默认值参数的情况下用的比较多
// 在有大量参数有默认值,但想覆盖其中一个或数个的情况下较为方便
test4(name="test1")
}

1.5 函数至简原则

函数至简原则:能省则省

1)至简原则细节

(1)return 可以省略,Scala 会使用函数体的最后一行代码作为返回值

(2)如果函数体只有一行代码,可以省略花括号

(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)

(4)如果有 return,则不能省略返回值类型,必须指定

(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用

(6)Scala 如果期望是无返回值类型,可以省略等号

(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加

(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略

(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略

2)案例

// (0)函数标准写法
def f(s: String): String = {
    
    
    return s
}
println(f("Hello"))
// 至简原则:能省则省
//(1) return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
def f1(s: String): String = {
    
    
    s
}
println(f1("Hello"))
//(2)如果函数体只有一行代码,可以省略花括号
def f2(s: String):String = s
//(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
def f3(s: String) = s
println(f3("Hello"))
//(4)如果有 return,则不能省略返回值类型,必须指定。
def f4(): String = {
    
    
    return "test"
}
println(f4())
//(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
def f5(): Unit = {
    
    
    return "test"
}
println(f5())
//(6)Scala 如果期望是无返回值类型,可以省略等号
// 将无返回值的函数称之为过程
def f6(s: String) {
    
    
    println(s)
}
println(f6("test"))	// test
// ()
// 注:在此处返回值为(),即空
//(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
def f7() = "test"
println(f7())
println(f7)
//(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
def f8 = "test"
// println(f8())
println(f8)
//(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
// 匿名函数
(x:String) => {
    
     println(x) }
def f9 = (x: String) => {
    
     println(x) }
def f10(f: String => Unit) = {
    
    
    f("")
}
f10(f9)
println(f10((x: String) => {
    
    println("test")}))

2.函数高级

2.1 高阶函数

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

def main(args: Array[String]): Unit = {
    
    
    //(1)调用 foo 函数,把返回值给变量 f
    //val f = foo()
    val f = foo
    println(f)	// foo	相当于调用
    // 1
    //(2)在被调用函数 foo 后面加上 _,相当于把函数 foo 当成一个整体,传递给变量 f1
    val f1 = foo _	// 在此处加了个下划线,即是赋值,如果输出不会进行调用,而会输出函数对象
    foo()	// foo
    f1()	// foo 注:如果输出f1,即是(包名.对象名$$$Lambda$5/1782704802@7cd62f43)
    //(3)如果明确变量类型为函数类型,那么不使用下划线也可以将函数作为整体传递给变量
    var f2:()=>Int = foo // 相当于foo _
}

def foo():Int = {
    
    
    println("foo")
    1
}

2)函数可以作为参数进行传递
将函数作为参数可以使同一个输入通过一个函数实现不同的操作,即只要作为参数的函数的类型满足,就可以通过此函数去完成不同的操作

例如:

func(f: (Int,Int) => Int) 函数

add(Int,Int) => Int实现相加功能

multiply(Int,Int) => Int实现相乘功能

add函数与multiply函数都满足func函数参数的要求,即func(add(Int,Int))与func(multiply(Int,Int))皆是正确的,但二者所实现的功能是不同的,则实现类似于func函数可以较为灵活地实现一个函数完成多种功能,而且传入符合条件的匿名函数也是可行的。

def main(args: Array[String]): Unit = {
    
    
    // (1)定义一个函数,函数参数还是一个函数签名;f 表示函数名称;(Int,Int)表示输入两个 Int 参数;Int 表示函数返回值
    // f: (Int, Int) => Int 即参数:(Int, Int) 返回值:Int的函数
    def f1(f: (Int, Int) => Int, a: Int, b: Int): Int = {
    
    
        f(a, b)
    }

    // (2)定义一个函数,参数和返回值类型和 f1 的输入参数一致
    def add(a: Int, b: Int): Int = a + b

    // (3)将 add 函数作为参数传递给 f1 函数,如果能够推断出来不是调用,_可以省略
    println(f1(add, 3, 4))	// 7
    println(f1(add _, 2, 4))	// 6
    //可以传递匿名函数
}

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

def main(args: Array[String]): Unit = {
    
    
    def f1() = {
    
    
        def f2() = {
    
    
        }
        // 函数名 + _,表示函数本身,在此处表示返回f2函数本身
        f2 _
    }
    val f = f1()
    // 因为 f1 函数的返回值依然为函数,所以可以变量 f 可以作为函数继续调用
    f()
    // 上面的代码可以简化为
    f1()()
}

2.2 匿名函数

1)说明

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

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

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

2)案例

需求 1:传递的函数有一个参数

传递匿名函数至简原则:

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

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

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

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

def main(args: Array[String]): Unit = {
    
    
    // (1)定义一个函数:参数包含数据和逻辑函数
    def operation(arr: Array[Int], op: Int => Int) = {
    
    
        for (elem <- arr) yield op(elem)
    }
    // (2)定义逻辑函数
    def op(ele: Int): Int = {
    
    
        ele + 1
    }
    // (3)标准函数调用
    val arr = operation(Array(1, 2, 3, 4), op)
    println(arr.mkString(","))	// 2,3,4,5
    // (4)采用匿名函数
    val arr1 = operation(Array(1, 2, 3, 4), (ele: Int) => {
    
    
        ele + 1
    })
    println(arr1.mkString(","))	// 2,3,4,5
    // (4.1)参数的类型可以省略,会根据形参进行自动的推导;
    val arr2 = operation(Array(1, 2, 3, 4), (ele) => {
    
    
        ele + 1
    })
    println(arr2.mkString(","))	// 2,3,4,5
    // (4.2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。
    val arr3 = operation(Array(1, 2, 3, 4), ele => {
    
    
        ele + 1
    })
    println(arr3.mkString(","))	// 2,3,4,5
    // (4.3) 匿名函数如果只有一行,则大括号也可以省略
    val arr4 = operation(Array(1, 2, 3, 4), ele => ele + 1)
    println(arr4.mkString(","))	// 2,3,4,5
    //(4.4)如果参数只出现一次,则参数省略且后面参数可以用_代替
    val arr5 = operation(Array(1, 2, 3, 4), _ + 1)
    println(arr5.mkString(","))	// 2,3,4,5
}
def main(args: Array[String]): Unit = {
    
    
    def dualFunctionOneAndTwo(fun: (Int, Int) => Int): Int = {
    
    
        fun(1, 2)
    }

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

    // 方式一:常见用法
    println(dualFunctionOneAndTwo(add))
    println(dualFunctionOneAndTwo(minus))

    // 匿名函数简化
    // 方式二:匿名函数直接填入
    println(dualFunctionOneAndTwo((a: Int, b: Int) => a + b))
    println(dualFunctionOneAndTwo((a: Int, b: Int) => a - b))

    // 方式三:由于a,b的类型在定义时已经确定下来,故而可以省略
    println(dualFunctionOneAndTwo((a, b) => a + b))
    println(dualFunctionOneAndTwo((a, b) => a - b))

    // 方式四:由于a,b都只出现过一次,故可以进行省略
    println(dualFunctionOneAndTwo(_ + _))
    println(dualFunctionOneAndTwo(_ - _))

    // 若需计算b-a,则可以使用-a+b
    println(dualFunctionOneAndTwo((a, b) => -a + b))
    println(dualFunctionOneAndTwo(-_ + _))
}

2.3 高阶函数案例

模拟 Map 映射、Filter 过滤、Reduce 聚合

def main(args: Array[String]): Unit = {
    
    
    // (1)map 映射
    def map(arr: Array[Int], op: Int => Int) = {
    
    
        for (elem <- arr) yield op(elem)
    }
    val arr = map(Array(1, 2, 3, 4), (x: Int) => {
    
    
        x * x
    })
    println(arr.mkString(","))	// 1,4,9,16
    // (2)filter 过滤。有参数,且参数再后面只使用一次,则参数省略且后面参数用_表示
    def filter(arr:Array[Int],op:Int =>Boolean) = {
    
    
        val arr1:ArrayBuffer[Int] = ArrayBuffer[Int]()
        for(elem <- arr if op(elem)){
    
    
            arr1.append(elem)
        }
        arr1.toArray
    }
    val arr1 = filter(Array(1, 2, 3, 4), _ % 2 == 1)
    println(arr1.mkString(","))	// 1,3
    // (3)reduce 聚合。有多个参数,且每个参数再后面只使用一次,则参数省略且后面参数用_表示,第 n 个_代表第 n 个参数
    def reduce(arr: Array[Int], op: (Int, Int) => Int) = {
    
    
        var init: Int = arr(0)
        for (elem <- 1 until arr.length + 1) {
    
    
            init = op(init, elem)
        }
        init
    }
    val arr2 = reduce(Array(1, 2, 3, 4), _ * _)
    println(arr2)	// 24
}

定义一个匿名函数,并将它作为值赋给变量 fun。函数有三个参数,类型分别为 Int,String,Char,返回值类型为 Boolean。要求调用函数 fun(0, “”, ‘0’)得到返回值为 false,其它情况均返回 true。

def main(args: Array[String]): Unit = {
    
    
    val fun = (a: Int, b: String, c: Char) => if(a == 0 && b == "" && c == '0') false else true
    println(fun(0, "", '0'))	// false
    println(fun(1, "", '0'))	// true
}

定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。函数 f2 接收一个 Char 类型的参数,返回一个 Boolean 的值。要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true。

def main(args: Array[String]): Unit = {
    
    
    def func(a: Int) = {
    
    
        def f1(b: String) = {
    
    
            def f2(c: Char) = if(a == 0 && b == "" && c == '0') false else true
            f2 _
        }
        f1 _
    }

    println(func(0)("")('0'))	// false
    println(func(1)("")('0'))	// true
}

// 简写方式一
def func1(a: Int) = {
    
    
    (b: String) => {
    
    
        (c: Char) => if(a == 0 && b == "" && c == '0') false else true
    }
}

// 简写方式二
def func(a: Int): String => Char => Boolean = {
    
    
    b => c => if(a == 0 && b == "" && c == '0') false else true
}

2.4 函数柯里化&闭包

闭包:函数式编程的标配

1)说明

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

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

2)案例

def main(args: Array[String]): Unit = {
    
    
    def add(a: Int, b: Int): Int = {
    
    
        a + b
    }

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

    def addByFive(b: Int): Int = {
    
    
        5 + b
    }

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

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

    println(addByA(1)(2)) // 3

    // 新的场景,再加一个固定数时就无须固定,可灵活改变
    // 在此处只是闭包最简单的例子,并非简单的a+b的问题,要深入原理,以此原理扩展,可以更加灵活地处理很多逻辑
    val addByFour_2 = addByA(4)
    val addByFive_2 = addByA(5)

    println(addByFour_2(5)) // 9

    // 简写
    def addByA_2(a: Int): Int=>Int = {
    
    
        (b: Int) => {
    
    
            a + b
        }
    }

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

    def addByA_4(a: Int): Int=>Int = {
    
    
        a + _
    }
    
    def addByA_5(a: Int): Int=>Int = a + _
    
    // 柯里化
    def addCurring(a: Int)(b: Int): Int = {
    
    
      a + b
    }
    
    println(addCurring(1)(2))	// 3
}

2.5 递归

1)说明

一个函数/方法在函数/方法体内又调用了本身,称之为递归调用

  1. 方法调用自身

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

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

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

2)案例

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

// 递归实现阶乘
def fact(n: Int): Int = {
    
    
    if(n == 0) 1 else fact(n - 1) * n
}

// 尾递归:纯函数式编程语言或是类似于scala这种用函数式编程特性的语言对递归的优化
// 在最后一行返回的只有对自身的调用,即不是类似于递归的压栈,而是要再次调用时将上次栈弹出再将此次压入,因此不会产生栈溢出的情况
def tailFact(n: Int): Int = {
    
    
    // 将结果存储在一个变量中,则不需要再一直堆在栈中
    // @tailrec保证此为一个正确的尾递归程序,不正确则会报错
    @tailrec
    def loop(n: Int, currRes: Int): Int = {
    
    
        if(n == 0) currRes
        loop(n - 1, currRes * n)
    }
    loop(n, 1)
}

2.6 控制抽象

1)值调用:把计算后的值传递过去

def main(args: Array[String]): Unit = {
    
    
    def f(a: Int): Unit = {
    
    
        println("a: " + a)
    }

    def f1(): Int = {
    
    
        println("f1")
        12
    }

    f(f1())
}

2)名调用:把代码传递过去

def main(args: Array[String]): Unit = {
    
    
    def f1(): Int = {
    
    
        println("f1")
        12
    }

    // => Int 表示一段返回值为Int的代码块
    // Int=>Int 表示参数为Int,返回值为Int的代码块
    def f2(a: => Int): Unit = {
    
    
        println("a: " + a)
        println("a: " + a)
    }

    // 在此处f1执行了两次,由于参数是a,在f2中a出现了两次,故f1调用了两次
    // 在此处并非将结果传入,而是将代码块完整传入
    f2(f1())	// f1
    			// a: 12
    			// f1
    			// a: 12
    
    // 直接放入代码块也可行
    f2({
    
    
      println("代码块")
      12
    })
}

案例

// 用闭包实现一个函数,将代码块作为参数传入,递归调用
// 实现类似while循环
def main(args: Array[String]): Unit = {
    
    
    def myWhile(condition: =>Boolean): (=>Unit)=>Unit = {
    
    
        def doLoop(op: =>Unit): Unit = {
    
    
            if(condition) {
    
    
                op
                myWhile(condition)(op)
            }
        }
        doLoop _
    }

    var n = 10
    // 结果与使用while相同
    myWhile(n >= 1) {
    
    
        println(n)
        n -= 1
    }

    // 简写
    def myWhile_2(condition: =>Boolean)(op: => Unit): Unit = {
    
    
        if(condition) {
    
    
            op
            myWhile_2(condition)(op)
        }
    }

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

2.7 惰性加载

1)说明

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

注意: lazy 不能修饰 var 类型的变量

2)案例

def main(args: Array[String]): Unit = {
    
    
    lazy val result: Int = sum(13, 47)

    println("1,函数调用")
    println("2,result = " + result)
}

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

/* 输出如下所示 */
// 1,函数调用
// 3,sum调用
// 2,result = 60

猜你喜欢

转载自blog.csdn.net/treesorshining/article/details/127543575
今日推荐