Scala的函数式编程与高阶函数,匿名函数,偏函数,函数的闭包、柯里化,抽象控制,懒加载等

Scala的函数式编程

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

  • 例如:请求->用户名、密码->连接 JDBC->读取数据库
  • Scala 语言是一个完全函数式编程语言。万物皆函数。
  • 函数的本质:函数可以当做一个值进行传递。
  • 在 Scala 中函数式编程和面向对象编程完美融合在一起了。

函数的基本语法和使用

函数基本语法

在Scala中,函数是一等公民,可以被赋值给变量、作为参数传递给其他函数,以及作为返回值返回。

  1. 函数的基本语法:
    在这里插入图片描述
  2. 使用案例:
// 定义一个函数,计算两个整数的和
def add(a: Int, b: Int): Int = {a + b
}// 调用函数
val result = add(3, 4)
println(result) // 输出结果为7

函数使用def关键字进行定义,并且需要指定参数列表和返回类型。在函数体中,可以使用return关键字返回一个值,也可以省略return关键字,将最后一行作为返回值。

函数定义

  1. 函数定义

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

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

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

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

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

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

  1. 案例实操

下面示例函数的不同形式的定义:

object Test_FunctionDefine {def main(args: Array[String]): Unit = {//    (1)函数1:无参,无返回值def f1(): Unit = {println("1. 无参,无返回值")}f1()println(f1())println("=========================")//    (2)函数2:无参,有返回值def f2(): Int = {println("2. 无参,有返回值")return 12}println(f2())println("=========================")//    (3)函数3:有参,无返回值def f3(name: String): Unit = {println("3:有参,无返回值 " + name)}println(f3("alice"))println("=========================")//    (4)函数4:有参,有返回值def f4(name: String): String = {println("4:有参,有返回值 " + name)return "hi, " + name}println(f4("alice"))println("=========================")//    (5)函数5:多参,无返回值def f5(name1: String, name2: String): Unit = {println("5:多参,无返回值")println(s"${name1}${name2}都是我的好朋友")}f5("alice","bob")println("=========================")//    (6)函数6:多参,有返回值def f6(a: Int, b: Int): Int = {println("6:多参,有返回值")return a + b}println(f6(12, 37))}
}

函数和方法的区别

  1. 核心概念
    (1) 为完成某一功能的程序语句的集合,称为函数。
    (2) 类中的函数称之方法。
  2. 案例实操
    (1) Scala 语言可以在任何的语法结构中声明任何的语法
    (2) 函数没有重载和重写的概念;方法可以进行重载和重写
    (3) Scala 中函数可以嵌套定义。
object Test_FunctionAndMethod {def main(args: Array[String]): Unit = {// 定义函数def sayHi(name: String): Unit = {println("hi, " + name)}// 调用函数sayHi("alice")// 调用对象方法Test01_FunctionAndMethod.sayHi("bob")// 获取方法返回值val result = Test01_FunctionAndMethod.sayHello("cary")println(result)}// 定义对象的方法def sayHi(name: String): Unit = {println("Hi, " + name)}def sayHello(name: String): String = {println("Hello, " + name)return "Hello"}
}

Scala 中函数嵌套

object TestFunction {// (2)方法可以进行重载和重写,程序可以执行
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 ={println("函数可以嵌套定义")}}}
}

除了常规的函数定义,Scala还支持匿名函数、高阶函数、偏函数等概念,下面将逐一介绍。

匿名函数

匿名函数是一种没有命名的函数,它可以直接作为表达式传递给其他函数或赋值给变量。以下是匿名函数的示例:

// 定义一个函数,接受一个函数和两个整数作为参数,并将结果打印出来
def operate(f: (Int, Int) => Int, a: Int, b: Int): Unit = {val result = f(a, b)println(result)
}// 调用operate函数,并传递一个匿名函数作为参数
operate((a, b) => a + b, 3, 4) // 输出结果为7

上述示例中,匿名函数(a, b) => a + b接受两个整数并返回它们的和。将该匿名函数作为参数传递给operate函数后,可以在operate函数内部调用该匿名函数并得到结果。

高阶函数

高阶函数是指接受一个或多个函数作为参数,并/或返回一个函数的函数。在Scala中,高阶函数的定义和使用非常灵活,以下是高阶函数的语法规则和几种常见类型的示例:

高阶函数常见类型

1. 参数为函数的高阶函数:

def operate(f: (Int, Int) => Int, a: Int, b: Int): Int = {f(a, b)
}val add = (a: Int, b: Int) => a + b
val result = operate(add, 3, 4) // 调用operate函数,传入add函数作为参数
println(result) // 输出结果为7

在上述示例中,operate函数接受一个函数f作为参数,并将ab作为实参调用了f函数。

2. 函数返回值为函数的高阶函数:

def multiplyBy(factor: Int): Int => Int = {(x: Int) => x * factor
}val multiplyByTwo = multiplyBy(2) // 调用multiplyBy函数,返回一个新的函数
val result = multiplyByTwo(5) // 调用返回的新函数
println(result) // 输出结果为10

在上述示例中,multiplyBy函数返回了一个新的函数,新函数会将其参数与factor相乘。这里 Int => Int 代表参数为Int类型,返回值也为Int类型的函数,这种是对lambda表达式的简写。

3. 参数和返回值都为函数的高阶函数

def compose(f: Int => Int, g: Int => Int): Int => Int = {(x: Int) => f(g(x))
}
val addOne = (x: Int) => x + 1
val multiplyByTwo = (x: Int) => x * 2
val result = compose(addOne, multiplyByTwo)(3) // 调用compose函数,并传入两个函数作为参数
println(result) // 输出结果为7

在上述示例中,compose函数接受两个函数fg作为参数,并返回一个新的函数,新函数将f应用于g的结果。输出结果为7,函数执行的过程为:定义了两个函数分别为:addOne和multiplyByTwo,调用compose(f, g)函数,将addOne和multiplyByTwo函数作为参数传入,则返回一个新的函数,参数类型为一个Int,函数体为f(g(x)),那么返回的新函数为:addOne( multiplyByTwo ( x: Int ) ),调用compose(addOne, multiplyByTwo)(3)函数,x参数为3,里层函数为 multiplyByTwo(3) => 6,结果返给外层函数addOne(x: Int),最终结果7.


高阶函数是指接受一个或多个函数作为参数,并/或返回一个函数的函数。Scala提供了多种高阶函数,如mapflatMapfilter等。

scala常见的高阶函数及使用方法

  • map:对集合中的每个元素应用一个函数,并返回一个新的集合。
val numbers = List(1, 2, 3, 4, 5)
val squaredNumbers = numbers.map(x => x * x)
println(squaredNumbers) // 输出结果为List(1, 4, 9, 16, 25)
  • flatMap:将集合中的每个元素应用一个函数,并将结果展平为一个新的集合。
val words = List("Hello", "World")
val letters = words.flatMap(word => word.toCharArray)
println(letters) // 输出结果为List(H, e, l, l, o, W, o, r, l, d)
  • filter:根据给定条件过滤集合中的元素。
val numbers = List(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter(x => x % 2 == 0)
println(evenNumbers) // 输出结果为List(2, 4)
  • reduce:将集合中的元素逐个进行累积计算。
val numbers = List(1, 2, 3, 4, 5)
val sum = numbers.reduce((a, b) => a + b)
println(sum) // 输出结果为15
  • fold:将集合中的元素逐个进行累积计算,可以指定一个初始值。
val numbers = List(1, 2, 3, 4, 5)
val sum = numbers.fold(0)((a, b) => a + b)
println(sum) // 输出结果为15
  • groupBy:根据给定的函数对集合中的元素进行分组。
val numbers = List(1, 2, 3, 4, 5)
val groupedNumbers = numbers.groupBy(x => x % 2)
println(groupedNumbers) // 输出结果为Map(1 -> List(1, 3, 5), 0 -> List(2, 4))

通过使用这些常见的高阶函数,您可以以一种函数式的方式对集合进行处理和转换,简化代码,并提高代码的可读性和可维护性。

偏函数的使用

偏函数是一种只对部分输入值进行定义的函数。Scala的偏函数使用PartialFunction类表示。以下是一个使用偏函数的示例:

val divide: PartialFunction[Int, Int] = {case d: Int if d != 0 => 42 / d
}val result1 = divide(6)  // 输出结果为7
val result2 = divide(0)  // 抛出MatchError异常

上述示例中的偏函数divide定义了一个对整数进行除法运算的偏函数,只有当除数不为零时才能有效计算。


如果一个方法中没有match 只有case,这个函数可以定义成PartialFunction偏函数。偏函数定义时,不能使用括号传参,默认定义PartialFunction中传入一个值,匹配上了对应的case,返回一个值,只能匹配同种类型。

一个case语句就可以理解为是一段匿名函数。

/*** 一个函数中只有case 没有match ,可以定义成PartailFunction 偏函数*/
object Lesson_PartialFunction {def MyTest : PartialFunction[String,String] = {case "scala" =>{"scala"}case "hello" =>{"hello"}case _ => {"no  match ..."}}def main(args: Array[String]): Unit = {println(MyTest("scala"))}
}
/*** 第二个例子* map和collect的区别。*/
def main(args: Array[String]): Unit = {val list1 = List(1, 3, 5, "seven") map {MyTest}//List(1, 3, 5, "seven") map { case i: Int => i + 1 }list1.foreach(println)val list = List(1, 3, 5, "seven") collect {MyTest}//List(1, 3, 5, "seven") collect { case i: Int => i + 1 }list.foreach(println)
}
def MyTest: PartialFunction[Any, Int] = {case i: Int => i + 1
}

函数的柯里化、抽象控制和惰性加载

柯里化(Currying)

  • 柯里化(Currying):柯里化是将接受多个参数的函数转换为接受一个参数的函数序列的过程。这可以提供更高的灵活性和复用性。
def add(a: Int)(b: Int): Int = a + bval addTwo = add(2) _  // 创建一个新函数,固定a为2
val result = addTwo(3) // 输出结果为5

抽象控制

  • 抽象控制:抽象控制是指接受函数作为参数,并在需要的时候调用该函数。它提供了一种灵活的控制流程。
def executeFunction(callback: => Unit): Unit = {println("执行前")callbackprintln("执行后")
}executeFunction {println("回调函数被执行")
}

上述示例中的executeFunction函数接受一个没有参数和返回值的函数,并在适当的时机调用该函数。

抽象控制例子二

object Test_ControlAbstraction {def main(args: Array[String]): Unit = {// 1. 传值参数def f0(a: Int): Unit = {println("a: " + a)println("a: " + a)}f0(23)def f1(): Int = {println("f1调用")12}f0(f1())println("========================")// 2. 传名参数,传递的不再是具体的值,而是代码块def f2(a: =>Int): Unit = {println("a: " + a)println("a: " + a)}f2(23)f2(f1())f2({println("这是一个代码块")29})}
}

函数的懒加载机制

  • 惰性加载:惰性加载是指推迟表达式的求值,直到它被实际使用时才计算。这有助于提高程序效率和资源利用率。
lazy val expensiveCalculation: Int = {// 执行昂贵的计算42
}println(expensiveCalculation) // 在此处才进行计算和输出结果

在上述示例中,expensiveCalculation变量在声明时并不会立即计算,而是在第一次访问时才进行计算。

柯里化和闭包示例

/** 在scala中,函数是一个对象,方法调用的时候,会在内存中开辟两个空间,* 一个是栈内存,方法调用会压栈,同时会将方法对应的函数实例保存到堆内存一份,* 对内存的数据是共享的。栈内存是独享的,随着方法调用完毕,栈内存会释放,* 但是堆内存的变量仍然还在,这样就实现了函数的闭包。*/
object Test_ClosureAndCurrying {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 addByFive(b: Int): Int = {5 + b}// 3. 将固定加数作为另一个参数传入,但是是作为”第一层参数“传入def addByFour1(): Int=>Int = {val a = 4def addB(b: Int): Int = {a + b}addB}def addByA(a: Int): Int=>Int = {def addB(b: Int): Int = {a + b}addB}println(addByA(35)(24))val addByFour2 = addByA(4)val addByFive2 = addByA(5)println(addByFour2(13))println(addByFive2(25))// 4. 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 = addByA3(4)val addByFive3 = addByA3(5)println(addByFour3(13))println(addByFive3(25))// 5. 柯里化def addCurrying(a: Int)(b: Int): Int = {a + b}println(addCurrying(35)(24))}
}

总结:

Scala是一门强大的函数式编程语言,提供丰富的函数概念。本文首先介绍了函数的基本语法和使用方法,并列举了一些示例。然后,讨论了匿名函数的特点以及如何使用它们。接下来,我们介绍了高阶函数及其常见用法。然后,我们介绍了偏函数的概念和使用方法。最后,我们涵盖了柯里化、抽象控制和惰性加载等进阶主题。

函数的闭包

函数闭包是指一个函数以及其在创建时所能访问的自由变量(即不是参数也不是局部变量)的组合。闭包允许函数捕获并访问其周围上下文中定义的变量,即使这些变量在函数执行时不再存在。

在Scala中,闭包是一种非常强大且常见的特性,它可以用于创建具有灵活状态的函数。以下是一个示例来说明函数闭包的概念:

def multiplier(factor: Int): Int => Int = {(x: Int) => x * factor
}val multiplyByTwo = multiplier(2) // 使用multiplier函数创建一个闭包
val result1 = multiplyByTwo(5) // 调用闭包
println(result1) // 输出结果为10val multiplyByThree = multiplier(3) // 使用multiplier函数创建另一个闭包
val result2 = multiplyByThree(5) // 调用另一个闭包
println(result2) // 输出结果为15

在上述示例中,multiplier函数接受一个整数参数factor,并返回一个闭包,闭包是一个匿名函数(x: Int) => x * factor。在闭包中,factor是一个自由变量,它被捕获并保存在闭包中,即使在闭包被调用时,factor的定义已经超出了其作用域。因此,每次调用闭包时,它都可以访问和使用正确的factor值,实现了状态的保留。

闭包在许多场景下非常有用,例如:

  • 创建高阶函数时,可以使用闭包将函数作为参数传递,并携带额外的上下文信息。
  • 在函数式编程中,闭包可以用于构建递归函数,其中函数自身作为参数传递给递归调用。
  • 在异步编程中,使用闭包可以捕获异步操作完成后的结果,并进行后续处理。

需要注意的是,在使用闭包时,需要谨慎处理自由变量的生命周期,确保不会发生对已经超出作用域的变量的意外引用。另外,闭包可能会引起内存泄漏,因此需要适度使用,避免额外的资源占用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/117810.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode】1654:到家的最少跳跃次数的解题思路 关于力扣无法return的BUG的讨论

文章目录 一、题目二、题解与代码三、神奇的BUG3.1 无法执行的 return 和 break 语句3.2 通过另一个 break 解决 一、题目 有一只跳蚤的家在数轴上的位置 x 处。请你帮助它从位置 0 出发,到达它的家。 跳蚤跳跃的规则如下: 它可以 往前 跳恰好 a 个位…

【C++深入浅出】类和对象上篇(类的基础、类的模型以及this指针)

目录 一. 前言 二. 面向对象与面向过程 2.1 面向过程 2.2 面向对象 三. 类的基础知识 3.1 类的引入 3.2 类的定义 3.3 成员变量的命名规则 3.4 封装 3.5 类的访问限定符 3.6 类的作用域 3.7 类的实例化 四. 类的对象模型 4.1 类对象的大小 4.2 类对象的存储方式 …

在 WSL2 中使用 NVIDIA Docker 进行全栈开发和深度学习 TensorFlow pytorch GPU 加速

在 WSL2 中使用 NVIDIA Docker 进行全栈开发和深度学习 TensorFlow pytorch GPU 加速 0. 背景 0.1 起源 生产环境都是在 k8d pod 中运行,直接在容器中开发不好嘛?每次换电脑,都要配配配,呸呸呸新电脑只安装日常用的软件不好嘛&…

【从0学习Solidity】合约入门 Hello Web3

【学习Solidity的基础】入门智能合约开发 Hello Web3 📱不写代码没饭吃上架主页 在强者的眼中,没有最好,只有更好。我们是全栈开发领域的优质创作者,同时也是阿里云专家博主。 ✨ 关注我们的主页,探索全栈开发的无限…

【斗罗Ⅱ】最强武魂揭秘,98级玄老、95级言少哲神兽级武魂曝光

Hello,小伙伴们,我是小郑继续为大家深度解析【绝世唐门】 在斗罗大陆动画绝世唐门中,98级玄老已经登场,他是一个很随意的老人,乍眼一看,似乎是一个邋里邋遢、好吃懒做的人,但是实际上他却是史莱克学院重量级…

Linux(CentOS7)下如何配置多个Tomcat容器?

一、在 liunx 系统安装 jdk 1、安装jdk(yum install 安装) 查看是否系统是否自带jdk并卸载 rpm -qa |grep java rpm -qa |grep jdk rpm -qa |grep gcj 其中,GCJ是GNU的Java编译器,可以把java程序编译成本地代码,编译成功后的可…

JixiPix Artista Impresso Pro for mac(油画滤镜效果软件)

JixiPix Artista Impresso pro Mac是一款专业的图像编辑软件,专为Mac用户设计。它提供了各种高质量的图像编辑工具,可以帮助您创建令人惊叹的图像。该软件具有直观的用户界面,使您可以轻松地浏览和使用各种工具。 它还支持多种文件格式&…

说说IO多路复用

分析&回答 IO多路复用 I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流。直白点说:多路指的是多个socket连接,复用指的是复用一个…

非科班菜鸡算法学习记录 | 代码随想录算法训练营第53天|| 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 动态规划

1143. 最长公共子序列 知识点:动规 状态:不会 思路: 用dpij表示两个串中到i-1和j-1个字符结束的最长公共子序列长度(不用特殊初始化) class Solution { public:int longestCommonSubsequence(string text1, string …

Web of Science批量导出

目录 如何用Web of Science检索学术信息问题批量导出 Web of Science检索结果 如何用Web of Science检索学术信息 进入 Web of Science 检索页面: https://www.webofscience.com/wos/woscc/basic-search 根据需求填写过滤条件,点击 search 进入搜索详…

C# char曲线控件

一、char曲线显示随机数数据 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Syst…

完全平方数

题目链接 完全平方数 题目描述 注意点 返回 和为 n 的完全平方数的最少数量 解答思路 初始想到使用动态规划,后续数字的完全平方数可以由前面数字的完全平方数求得,对于任意数字,可以计算其减去从1…i之间(保证做减操作后的值…

【操作系统】一文快速入门,很适合JAVA后端看

作者简介: 目录 1.概述 2.CPU管理 3.内存管理 4.IO管理 1.概述 操作系统可以看作一个计算机的管理系统,对计算机的硬件资源提供了一套完整的管理解决方案。计算机的硬件组成有五大模块:运算器、控制器、存储器、输入设备、输出设备。操作…

Kubernetes技术--k8s核心技术 Secret

1.概述 Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec中。Secret可以以 Volume 或者环境变量的方式使用。 作用 加密数据存储在/etc中,使得pod容器以挂载volume方式进行访问。在进行的数据存储中是以base64加密的方式…

SQL 语句学习总结:

1. 四范式&&范式好处: 数据库范式是数据表设计的规范,在范式规范下,数据库里每个表存储的重复数据降到最少(这有助于数据的一致性维护),同时在数据库范式下,表和表之间不再有很强的数据…

服务器数据恢复-vmware ESXI虚拟机数据恢复案例

服务器数据恢复环境: 从物理机迁移一台虚拟机到ESXI,迁移后做了一个快照。该虚拟机上部署了一个SQLServer数据库,存放了5年左右的数据。ESXI上有数十台虚拟机,EXSI连接了一台EVA存储,所有的虚拟机都在EVA存储上。 服务…

【综述】跨模态可信感知

文章目录 跨模态可信感知综述摘要引言跨协议通信模式PCP网络架构 跨模态可信感知跨模态可信感知的概念跨模态可信感知的热点研究场景目前存在的挑战可能改进的方案 参考文献 跨模态可信感知综述 摘要 随着人工智能相关理论和技术的崛起,通信和感知领域的研究引入了…

Linux系统下的zabbix监控平台(单机安装服务)

目录 一、zabbix的基本概述 二、zabbix构成 1.server 2.web页面 3.数据库 4.proxy 5.Agent 三、监控对象 四、zabbix的日常术语 1.主机(host) 2.主机组(host group) 3.监控项(item) 4.触发器(trigger) 5.事件(event) 6.动作(a…

Aspose导出word使用记录

背景:Aspose系列的控件,功能实现都比较强大,可以实现多样化的报表设计及输出。 通过这次业务机会,锂宝碳审核中业务功需要实现Word文档表格的动态导出功能,因此学习了相关内容,在学习和参考了官方API文档的…

PSP - 蛋白质结构预测 OpenFold Multimer 训练模型的数据加载

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/132597659 OpenFold Multimer 是基于深度学习的方法,预测蛋白质的多聚体结构和相互作用。利用大规模的蛋白质序列和结构数据&#xff…