Scala深入学习之函数式编程

简介: 笔记

一、函数式编程


示例代码:

package matchDemo.function
/**
 * @author : 蔡政洁
 * @email :caizhengjie888@icloud.com
 * @date : 2020/11/17
 * @time : 6:25 下午
 * scala函数式编程
 */
object FunctionOps1 {
    def main(args: Array[String]): Unit = {
        // 作为值的函数
        funcOps1()
        // 匿名函数
        funcOps2()
        // 高阶函数
        funcOps3()
    }
    /*
    函数可以作为值,传递给另外一个变量,或者另外一个函数
    语法特点,必须要在函数后面加上空格或者下划线
     */
    def funcOps1(): Unit ={
        def sayGoodBye(name:String): Unit ={
            println("good bye to:"+name)
        }
        sayGoodBye("jack")
        // 传递给另外一个变量,用变量来调用
        val sayBye = sayGoodBye _
        sayBye("alex")
        //传递给另外一个函数
        def sayBeyBey = sayGoodBye _
        sayBeyBey("lili")
    }
    /*
    所谓匿名函数,其实就是没有名字的函数
    Scala定义一个完整函数:
        def funcName(params):returnType = {body}
    Scala定义一个匿名函数:
        (params) => {body}  匿名函数的实现
        (params) => returnType  匿名函数的定义
    问题是该匿名函数如何被调用
        --> 只能将匿名函数赋值给另外一个变量或者函数来调用
     */
    def funcOps2(): Unit ={
        // 定义一个匿名函数,并赋值给一个变量
        val sayBey = (name:String) => {
            println("good bye to:"+name)
        }
        // 调用匿名函数
        sayBey("alex")
        def sayBeyBey = (name:String) => {
            println("good bye to:"+name)
        }
        sayBeyBey("jone")
    }
    /*
    所谓高阶函数:带函数参数的函数,一个函数的参数是函数,把这种函数称之为高阶函数(high level function)
     */
    def funcOps3(): Unit ={
        // 定义一个高阶函数
        def sayBey(name:String,func:(String) => Unit): Unit ={
            func(name)
        }
        // 调用函数sayBey
        sayBey("alex",(name:String) => method(name))
        // 传递匿名函数
        sayBey("pony",(name:String) => println(name))
        // 简化书写
        // 如果匿名函数中只有一个变量,可以省略掉()
        sayBey("cheery",name => println(name))
        // 可以使用通配符"_"代替变量
        sayBey("candy", println(_))
        // 最简单可以连通配符"_"省略
        sayBey("luzy", println)
        // 重新在调用上述函数
        sayBey("gucci",method)
    }
    def method(str:String): Unit ={
        println("一日之计在于晨")
        println(str)
        println("一年之计在于春")
    }
}

运行结果:

good bye to:jack
good bye to:alex
good bye to:lili
good bye to:alex
good bye to:jone
一日之计在于晨
alex
一年之计在于春
pony
cheery
candy
luzy
一日之计在于晨
gucci
一年之计在于春


二、高阶函数


示例代码:

package matchDemo.function
/**
 * @author : 蔡政洁
 * @email :caizhengjie888@icloud.com
 * @date : 2020/11/18
 * @time : 10:33 上午
 * Scala中常见的高阶函数,这些高阶函数都是作用在集合上面
 * filter
 * map
 * flatmap
 * reduce
 * foreach
 * dropWhile
 * sortWith
 * groupBy
 * partitionBy
 */
object FunctionOps2 {
    def main(args: Array[String]): Unit = {
        filterOps()
        mapOps()
        flatMapOps()
        reduceOps()
        dropWhileOps()
        sortWithOps()
        groupByOps()
        partitionOps()
    }
    /*
    filter:过滤
    filter:(A => Boolean)
    过滤掉集合中元素A,经过函数操作,返回值为false的元素
     */
    def filterOps(): Unit ={
        // 过滤掉集合中的偶数,留下集合中的奇数
        val array = 1 to 10
        var filtered = array.filter((num:Int) => num%2 != 0)
        println(filtered.mkString("[",",","]"))
        // 省略推断类型
        filtered = array.filter((num) => num%2 != 0)
        println(filtered.mkString("[",",","]"))
        // 省略括号
        filtered = array.filter(num => num%2 != 0)
        println(filtered.mkString("[",",","]"))
        // 使用通配符
        filtered = array.filter(_ % 2 != 0)
        println(filtered.mkString("[",",","]"))
    }
    /*
    map:(p:A => B)
    将一个集合中的所有元素A,都要作用在该匿名函数p上,每一个元素调用一次该匿名函数,将元素A最终转换成元素B
    需要注意的是,A和B都是数据类型可以一致,也可以不一致
    map操作其实就是一个one-2-one的映射操作
     */
    def mapOps(): Unit ={
        val array = 1 to 5
        // 将集合中的每一个元素扩大1.5倍
        var newArr:IndexedSeq[Double] = array.map((num:Int) => num*1.5)
        println(newArr.mkString("[",",","]"))
        // 省略推断类型
        newArr = array.map((num) => num*1.5)
        println(newArr.mkString("[",",","]"))
        // 省略括号
        newArr = array.map(num => num*1.5)
        println(newArr.mkString("[",",","]"))
        // 使用通配符
        newArr = array.map(_ * 1.5)
        println(newArr.mkString("[",",","]"))
    }
    /*
    flatmap ---> (f:A => Iterable(B))
    flatmap和map的操作,比较相似,不同之处在于作用于匿名函数的返回值类型不一样
    map操作是one-2-one
    flatmap操作是one-2-many,类似数据库中列转行的操作
    ----------------------------------------------------------
    foreach(p:A => Unit)
    遍历集合中每一个元素A,进行操作,返回值类型为Unit
     */
    def flatMapOps(): Unit ={
        val array = Array(
            "i think there i am",
            "you are a lucky dog",
            "you are a green eye guy"
        )
        // 提取集合中的每一个单词
        var words = array.flatMap((line:String) => line.split(" "))
        // 查看集合word中的数据,遍历
        for(word <- words){
            print(word+" ")
        }
        println("\r\n-----------------")
        // 省略推断类型
        words = array.flatMap((line) => line.split(" "))
        // 使用foreach遍历
        words.foreach((word:String) => print(word + " "))
        println("\r\n-----------------")
        // 使用通配符
        words = array.flatMap(_.split(" "))
        // 使用foreach遍历
        words.foreach((word:String) => println(word + " "))
    }
    /*
    reduce(p:(A1,A2) => A3)
    reduce是一个聚合函数,将2个A转换成1个A(其作用就是,作用在集合中的元素,进行聚合操作,得到一个新的值)
    var sum = 0
        for (i <- array){
            sum = sum + i
        }
     此时,在每一次计算过程中的sum就是这里的A1,而i就是A2,sum和i聚合结束后的结果就是A3
     此时的A3又作为下一次聚合操作中的sum,也就是A1
     */
    def reduceOps(): Unit ={
        val array = 1 to 6
        var sum = 0
        for (i <- array){
            println(s"sum=${sum},i=${i}")
            sum = sum + i
        }
        println("final sum is:"+sum)
        println("-----------------------------")
        sum = array.reduce((A1:Int,A2:Int) => {
            println(s"sum=${A1},i=${A2}")
            A1+A2
        })
        println("final sum is:"+sum)
        println("-----------------------------")
        sum = array.reduce((A1,A2) => {
            A1+A2
        })
        println("final sum is:"+sum)
        println("-----------------------------")
        sum = array.reduce(_ + _) // 最简形式
        println("final sum is:"+sum)
    }
    /*
    dropWhile(p:A => Boolean)
    该函数和filter一样,作用在集合的每一个元素,返回值为boolean类型
    dropWhile删除元素,直到不满足条件位置
     */
    def dropWhileOps(): Unit ={
        val array = Array(3,-6,7,1,3,4,-8,2)
        // 循环删除其中的偶数,直到条件不满足
        var newArr = array.dropWhile((num:Int) => num%2 != 0)
        println(newArr.mkString("[",",","]"))
    }
    /*
    sortWith:排序操作
    sortWith((A,A) => boolean)
    返回值是boolean类型,同时sortWith是排序,显示就是要对这两个元素A进行大小比较,比较的结果就是boolean
     */
    def sortWithOps(): Unit ={
        val array = Array(3,-6,7,1,3,4,-8,2)
        // 对集合中的元素进行升序排序
        var newArr = array.sortWith((v1,v2) => v1<v2)
        println(newArr.mkString("[",",","]"))
        newArr = array.sortWith(_ > _)
        println(newArr.mkString("[",",","]"))
    }
    /*
    groupBy就是SQL中的group by
     */
    def groupByOps(): Unit = {
        val array = Array(
            "广东,广州",
            "广东,深圳",
            "广东,珠海",
            "浙江,杭州",
            "江苏,南京",
            "江苏,宿迁",
            "江苏,苏州",
            "湖北,武汉",
            "福建,福州",
            "江西,南昌",
            "山东,济南",
            "山东,青岛"
        )
        /*
        按照省份,对每一个城市进行分组
        用sql:
        select province,city from xxx group by province;
         */
        val province2Info:Map[String,Array[String]] = array.groupBy(line => line.substring(0,line.indexOf(",")))
        for ((province,cities) <- province2Info){
            println(s"province:${province}--->${cities.mkString("[",",","]")}")
        }
        println("------------grouped------------------")
        // 将原来的集合进行分组,每个组内的元素有两个,或者可以理解为均分集合,每一份都是一个组
        val arrays = array.grouped(2)
        for (arr <- arrays){
            println(arr.mkString("[",",","]"))
        }
    }
    /*
    将集合中的元素,按照一定的条件进行分组,最后分成两个组
    partitionBy(p:A => boolean):(集合A,集合B)
    在每一个元素A上进行作用该匿名函数,满足条件,共同构成返回值集合A, 不满足条件的共同构成返回值集合B
    最后达到分区效果
     */
    def partitionOps(): Unit ={
        val array = Array(3,-6,7,1,3,4,-8,2)
        // 将集合array,按照4进行分区,小于4的在一个分区中,大于等于4的在另一个分区
        val (left,right) = array.partition((num:Int) => num <4)
        println(left.mkString("[",",","]"))
        println(right.mkString("[",",","]"))
    }
 }

运行结果:

[1,3,5,7,9]
[1,3,5,7,9]
[1,3,5,7,9]
[1,3,5,7,9]
[1.5,3.0,4.5,6.0,7.5]
[1.5,3.0,4.5,6.0,7.5]
[1.5,3.0,4.5,6.0,7.5]
[1.5,3.0,4.5,6.0,7.5]
i think there i am you are a lucky dog you are a green eye guy 
-----------------
i think there i am you are a lucky dog you are a green eye guy 
-----------------
i 
think 
there 
i 
am 
you 
are 
a 
lucky 
dog 
you 
are 
a 
green 
eye 
guy 
sum=0,i=1
sum=1,i=2
sum=3,i=3
sum=6,i=4
sum=10,i=5
sum=15,i=6
final sum is:21
-----------------------------
sum=1,i=2
sum=3,i=3
sum=6,i=4
sum=10,i=5
sum=15,i=6
final sum is:21
-----------------------------
final sum is:21
-----------------------------
final sum is:21
[-6,7,1,3,4,-8,2]
[-8,-6,1,2,3,3,4,7]
[7,4,3,3,2,1,-6,-8]
province:浙江--->[浙江,杭州]
province:广东--->[广东,广州,广东,深圳,广东,珠海]
province:江苏--->[江苏,南京,江苏,宿迁,江苏,苏州]
province:福建--->[福建,福州]
province:湖北--->[湖北,武汉]
province:江西--->[江西,南昌]
province:山东--->[山东,济南,山东,青岛]
------------grouped------------------
[广东,广州,广东,深圳]
[广东,珠海,浙江,杭州]
[江苏,南京,江苏,宿迁]
[江苏,苏州,湖北,武汉]
[福建,福州,江西,南昌]
[山东,济南,山东,青岛]
[3,-6,1,3,-8,2]
[7,4]


三、闭包和柯里化


示例代码:

package matchDemo.function
/**
 * @author : 蔡政洁
 * @email :caizhengjie888@icloud.com
 * @date : 2020/11/18
 * @time : 4:49 下午
 * scala函数操作之
 *      闭包
 *      柯里化
 */
object FunctionOps3 {
    def main(args: Array[String]): Unit = {
        // 闭包
        closureOps()
        // 柯里化
        curringOps()
    }
    def closureOps(): Unit ={
        // 定义一个匿名函数,赋值给了一个有参的函数,匿名函数内部的实现需要前面定义的参数
        def mulBy(factor:Double) = (x:Double) => factor * x
        val triple = mulBy(3.0)
        val half = mulBy(0.5)
        /*
        mulBy的函数体是通过一个匿名函数来实现的
        而该匿名函数(x:Double) => factor * x,需要一个参数factor,并没有赋值,而该factor来自于前面的函数
        程序的执行过程:
        首先,在代码中定义了两个函数
            第一个是mulBy
            第二个是匿名函数(x:Double) => factor * x
         其次,执行函数mulBy,并赋值给triple,这样,首先就将factor值赋值成3.0
         函数执行完毕之后,是不是就进行弹栈,mulBy的实现匿名函数。
         再其次,匿名函数(x:Double) => factor * x压栈,factor是不是就将刚才的3.0保存到自己的局部变量中
         最后,调用它的triple(42)的时候,42传递给了x,最终结果就是42 * 3 = 126
         在这个过程中,最后,匿名函数调用了一个不再其作用于范围内的变量factor,先弹栈,然后将factor保存到了
         匿名函数,最后在匿名函数中调用了factor。
         把这种结构称之为闭包——函数可以在变量不处于其作用域范围内被调用。
         */
        println(triple(42) + "-------" + half(42))
    }
    /*
    柯里化:
        原来的函数有两个参数,def sum(x:Int,y:Int) = x+y
        现在将sum函数进行改变,只保留其中一个参数,另外一个参数作为一个新的匿名函数的参数列表而存在
        通常该sum函数的函数体就是该匿名函数
    把这个过程称之为柯里化
    一个很重要的操作就是降维,降低参数的个数,维度
     */
    def curringOps(): Unit ={
        def sum(x:Int,y:Int) = x+y
        // 进行柯里化改造
        def total(x:Int) = (y:Int) => x+y
        println(total(3)(4))
    }
}

运行结果:

126.0-------21.0
7
相关文章
|
5天前
|
消息中间件 分布式计算 Java
Scala函数式编程【从基础到高级】
Scala函数式编程【从基础到高级】
|
5天前
|
人工智能 安全 人机交互
Scala 05 —— 函数式编程底层逻辑
Scala讲座探讨了函数式编程的底层逻辑,强调无副作用和确定性。函数式编程的核心是纯函数,避免读写数据等副作用,将其移至代码边缘处理。函数输入输出应清晰定义,避免模糊参数。函数视为数据范畴间的映射,以范畴论为基础。业务逻辑转化为纯函数式,通过声明式编程实现解耦,关注输入输出而非过程,便于验证和自动编程。将业务逻辑视作流水线,每个函数处理数据,避免全局变量和`var`,优先确保正确性再优化效率。
11 1
Scala 05 —— 函数式编程底层逻辑
|
5天前
|
Scala 容器
Scala学习--day04--集合、常用方法、案例实操 - WordCount TopN、不同省份的商品点击排行
Scala学习--day04--集合、常用方法、案例实操 - WordCount TopN、不同省份的商品点击排行
|
5天前
|
消息中间件 分布式计算 大数据
Scala学习--day03--函数式编程
Scala学习--day03--函数式编程
|
5天前
|
Java Scala
Scala学习--day02---控制流、“九层妖塔—杨辉三角”
Scala学习--day02---控制流、“九层妖塔—杨辉三角”
|
5天前
|
Java 编译器 API
Scala学习--day01变量与数据类型、运算符
Scala学习--day01变量与数据类型、运算符
|
5天前
|
数据采集 监控 安全
通过Scala实现局域网监控上网记录分析:函数式编程的优雅之路
在当今数字时代,网络监控成为保障信息安全的必要手段之一。本文将介绍如何使用Scala编程语言实现局域网监控上网记录分析的功能,重点探讨函数式编程的优雅之路。通过一系列代码示例,我们将展示如何利用Scala的函数式特性和强大的语法来实现高效的监控和分析系统。
223 1
|
8月前
|
分布式计算 API Scala
Scala函数式编程
Scala函数式编程
48 0
|
12月前
|
分布式计算 Ubuntu Java