当前位置: 首页 > news >正文

Scala函数和闭包

1. 函数

1.1 函数与方法

Scala 中函数与方法的区别非常小,如果函数作为某个对象的成员,这样的函数被称为方法,否则就是一个正常的函数。

// 定义方法
def multi1(x:Int) = {x * x}
// 定义函数
val multi2 = (x: Int) => {x * x}println(multi1(3)) //输出 9
println(multi2(3)) //输出 9

也可以使用 def 定义函数:

def multi3 = (x: Int) => {* x}
println(multi3(3))  //输出 9 

multi2 和 multi3 本质上没有区别,这是因为函数是一等公民,val multi2 = (x: Int) => {x * x} 这个语句相当于是使用 def 预先定义了函数,之后赋值给变量 multi2。

1.2 函数类型

上面我们说过 multi2 和 multi3 本质上是一样的,那么作为函数它们是什么类型的?两者的类型实际上都是 Int => Int,前面一个 Int 代表输入参数类型,后面一个 Int 代表返回值类型。

scala> val multi2 = (x: Int) => {* x}
multi2: Int => Int = $$Lambda$1092/594363215@1dd1a777scala> def multi3 = (x: Int) => {* x}
multi3: Int => Int// 如果有多个参数,则类型为:(参数类型,参数类型 ...)=>返回值类型
scala> val multi4 = (x: Int,name: String) => {name + x * x }
multi4: (Int, String) => String = $$Lambda$1093/1039732747@2eb4fe7

1.3 一等公民&匿名函数

在 Scala 中函数是一等公民,这意味着不仅可以定义函数并调用它们,还可以将它们作为值进行传递:

import scala.math.ceil
object ScalaApp extends App {// 将函数 ceil 赋值给变量 fun,使用下划线 (_) 指明是 ceil 函数但不传递参数val fun = ceil _println(fun(2.3456))  //输出 3.0}

在 Scala 中你不必给每一个函数都命名,如 (x: Int) => 3 * x 就是一个匿名函数:

object ScalaApp extends App {// 1.匿名函数(x: Int) => 3 * x// 2.具名函数val fun = (x: Int) => 3 * x// 3.直接使用匿名函数val array01 = Array(1, 2, 3).map((x: Int) => 3 * x)  // 4.使用占位符简写匿名函数val array02 = Array(1, 2, 3).map(* 3)// 5.使用具名函数val array03 = Array(1, 2, 3).map(fun)}

1.4 特殊的函数表达式

1.4.1 可变长度参数列表

在 Java 中如果你想要传递可变长度的参数,需要使用 String ...args 这种形式,Scala 中等效的表达为 args: String*。

object ScalaApp extends App {
  def echo(args: String*): Unit = {for (arg <- args) println(arg)}echo("spark","hadoop","flink")
}
// 输出
spark
hadoop
flink

1.4.2 传递具名参数

向函数传递参数时候可以指定具体的参数名。

object ScalaApp extends App {  def detail(name: String, age: Int): Unit = println(name + ":" + age)// 1.按照参数定义的顺序传入detail("heibaiying", 12)// 2.传递参数的时候指定具体的名称,则不必遵循定义的顺序detail(age = 12, name = "heibaiying")}

1.4.3 默认值参数

在定义函数时,可以为参数指定默认值。

object ScalaApp extends App {
  def detail(name: String, age: Int = 88): Unit = println(name + ":" + age)// 如果没有传递 age 值,则使用默认值detail("heibaiying")detail("heibaiying", 12)
}

2. 闭包

2.1 闭包的定义

var more = 10
// addMore 一个闭包函数:因为其捕获了自由变量 more 从而闭合了该函数字面量
val addMore = (x: Int) => x + more

如上函数 addMore 中有两个变量 x 和 more:

x: 是一个绑定变量 (bound variable),因为其是该函数的入参,在函数的上下文中有明确的定义;

more: 是一个自由变量 (free variable),因为函数字面量本生并没有给 more 赋予任何含义。

按照定义:在创建函数时,如果需要捕获自由变量,那么包含指向被捕获变量的引用的函数就被称为闭包函数。

2.2 修改自由变量

这里需要注意的是,闭包捕获的是变量本身,即是对变量本身的引用,这意味着:

闭包外部对自由变量的修改,在闭包内部是可见的;

闭包内部对自由变量的修改,在闭包外部也是可见的。

// 声明 more 变量
scala> var more = 10
more: Int = 10// more 变量必须已经被声明,否则下面的语句会报错
scala> val addMore = (x: Int) => {+ more}
addMore: Int => Int = $$Lambda$1076/1844473121@876c4f0scala> addMore(10)
res7: Int = 20// 注意这里是给 more 变量赋值,而不是重新声明 more 变量
scala> more=1000
more: Int = 1000scala> addMore(10)
res8: Int = 1010

2.3 自由变量多副本

自由变量可能随着程序的改变而改变,从而产生多个副本,但是闭包永远指向创建时候有效的那个变量副本。

// 第一次声明 more 变量
scala> var more = 10
more: Int = 10// 创建闭包函数
scala> val addMore10 = (x: Int) => {+ more}
addMore10: Int => Int = $$Lambda$1077/1144251618@1bdaa13c// 调用闭包函数
scala> addMore10(9)
res9: Int = 19// 重新声明 more 变量
scala> var more = 100
more: Int = 100// 创建新的闭包函数
scala> val addMore100 = (x: Int) => {+ more}
addMore100: Int => Int = $$Lambda$1078/626955849@4d0be2ac// 引用的是重新声明 more 变量
scala> addMore100(9)
res10: Int = 109// 引用的还是第一次声明的 more 变量
scala> addMore10(9)
res11: Int = 19// 对于全局而言 more 还是 100
scala> more
res12: Int = 100

从上面的示例可以看出重新声明 more 后,全局的 more 的值是 100,但是对于闭包函数 addMore10 还是引用的是值为 10 的 more,这是由虚拟机来实现的,虚拟机会保证 more 变量在重新声明后,原来的被捕获的变量副本继续在堆上保持存活。

3. 高阶函数

3.1 使用函数作为参数

定义函数时候支持传入函数作为参数,此时新定义的函数被称为高阶函数。

object ScalaApp extends App {// 1.定义函数
  def square = (x: Int) => {
    x * x}// 2.定义高阶函数: 第一个参数是类型为 Int => Int 的函数
  def multi(fun: Int => Int, x: Int) = {fun(x) * 100}// 3.传入具名函数println(multi(square, 5)) // 输出 2500// 4.传入匿名函数println(multi(* 100, 5)) // 输出 50000}

3.2 函数柯里化

我们上面定义的函数都只支持一个参数列表,而柯里化函数则支持多个参数列表。柯里化指的是将原来接受两个参数的函数变成接受一个参数的函数的过程。新的函数以原有第二个参数作为参数。

object ScalaApp extends App {// 定义柯里化函数
  def curriedSum(x: Int)(y: Int) = x + yprintln(curriedSum(2)(3)) //输出 5
}

这里当你调用 curriedSum 时候,实际上是连着做了两次传统的函数调用,实际执行的柯里化过程如下:

第一次调用接收一个名为 x 的 Int 型参数,返回一个用于第二次调用的函数,假设 x 为 2,则返回函数 2+y;

返回的函数接收参数 y,并计算并返回值 2+3 的值。

想要获得柯里化的中间返回的函数其实也比较简单:

object ScalaApp extends App {// 定义柯里化函数
  def curriedSum(x: Int)(y: Int) = x + yprintln(curriedSum(2)(3)) //输出 5// 获取传入值为 10 返回的中间函数 10 + yval plus: Int => Int = curriedSum(10)_println(plus(3)) //输出值 13
}

柯里化支持多个参数列表,多个参数按照从左到右的顺序依次执行柯里化操作:

object ScalaApp extends App {// 定义柯里化函数
  def curriedSum(x: Int)(y: Int)(z: String) = x + y + zprintln(curriedSum(2)(3)("name")) // 输出 5name
}

相关文章:

Scala函数和闭包

1. 函数 1.1 函数与方法 Scala 中函数与方法的区别非常小&#xff0c;如果函数作为某个对象的成员&#xff0c;这样的函数被称为方法&#xff0c;否则就是一个正常的函数。 // 定义方法 def multi1(x:Int) {x * x} // 定义函数 val multi2 (x: Int) > {x * x}println(mult…...

LeetCode----1935. 可以输入的最大单词数

题目 键盘出现了一些故障,有些字母键无法正常工作。而键盘上所有其他键都能够正常工作。 给你一个由若干单词组成的字符串 text ,单词间由单个空格组成(不含前导和尾随空格);另有一个字符串 brokenLetters ,由所有已损坏的不同字母键组成,返回你可以使用此键盘完全输入…...

学习笔记三十:K8S配置管理中心Secret实现加密数据配置管理

K8S配置管理中心Secret实现加密数据配置管理 Secret概述secret三种可选参数:Secret类型 使用Secret通过环境变量引入Secret通过volume挂载Secret创建Secret创建yaml文件将Secret挂载到Volume中 Secret概述 Configmap一般是用来存放明文数据的&#xff0c;如配置文件&#xff0…...

关于uviewui修改主题及在uniapp中的应用

在uview使用过程中遇到很多不方便的地方&#xff0c;记录下来 修改主题颜色 给UI框架换个主题色基础方法是覆盖原有色&#xff08;但这个方法比较笨&#xff0c;处理起来也不干净利索&#xff09;&#xff0c;所以换个思路改变基础色值变量&#xff0c;步骤主要分为2部分&…...

使用QEMU模拟启动uboot

uboot的相关知识&#xff0c;可以参考&#xff1a;uboot基本概念。 一、环境配置 WSL: ubutu20.04 模拟开发板&#xff1a;vexpress-a9 uboot版本&#xff1a;u-boot-2023.10 二、安装QEMU 2.1、安装sudo apt install qemu2.2、查看支持哪些开发板qemu-system-arm -M help结…...

学习数据结构和算法之前,你需要知道什么?

最快的学习方法是什么&#xff1f;计算机基础支持有哪些&#xff1f;学习数据结构和算法应该如何思考&#xff1f;如何成长&#xff1f;为什么要学习数据结构和算法&#xff1f; 最快的学习方法是什么&#xff1f; 实践。 计算机基础支持有哪些&#xff1f; 数据结构和算法。…...

16. 机器学习 - 决策树

Hi&#xff0c;你好。我是茶桁。 在上一节课讲SVM之后&#xff0c;再给大家将一个新的分类模型「决策树」。我们直接开始正题。 决策树 我们从一个例子开始&#xff0c;来看下面这张图&#xff1a; 假设我们的x1 ~ x4是特征&#xff0c;y是最终的决定&#xff0c;打比方说是…...

将多余的内存,当作虚拟内存。修改edge缓存路径到虚拟内存中

一、下载工具&#xff0c;把内存映射成硬盘 软媒内存盘 v1.1.3.0 软媒内存盘下载-软媒内存盘 v1.1.3.0 - 下载吧 (xiazaiba.com) 二、映射edge的缓存路径 到新建的虚拟硬盘中 mklink /D "C:\Users\Administrator\AppData\Local\Microsoft\Edge\User Data" "V:\…...

【从0到1设计一个网关】过滤器链的实现---实现负载均衡过滤器

文章目录 什么是过滤器?编写负载均衡过滤器负载均衡的定义与实现负载均衡算法设计实现效果演示链接 自研网关整合Nacos,实现服务注册和配置变更 源码链接 什么是过滤器? 再前面的几个章节中我们已经实现了将我们的网关服务注册到注册中心,并且成功的从配置中心拉取了配置…...

科技云报道:打造生成式AI应用,什么才是关键?

科技云报道原创。 生成式AI作为当前人工智能的前沿领域&#xff0c;全球多家科技企业都在加大生成式AI的研发投入力度。 随着技术、产品及应用等方面不断推出重要成果&#xff0c;如今有更多的行业用户在思考该如何将生成式AI应用落地。 但开发生成式AI应用是一个充满挑战的…...

可回馈式电子负载的工作原理

可回馈式电子负载是一种用于模拟负载并测试电源或电子设备性能的工具。其工作原理如下&#xff1a; 控制回路&#xff1a;可回馈式电子负载内部有一个控制回路&#xff0c;用于监测和控制负载的电流、电压和功率等参数。这个控制回路可以根据用户设定的参数&#xff0c;自动调整…...

基于Vite使用VitePress搭建静态站点博客

使用VitePress搭建静态站点博客 官方文档什么是VitePress&#xff1f;一、初始化项目1.安装依赖包VitePress可以单独使用&#xff0c;也可以安装到现有的项目中。在这两种情况下&#xff0c;您都可以安装它: (也可以全局安装&#xff0c;建议&#xff1a;当前项目内安装) 2.初始…...

湖南互联网医院-让患者随时随地接受医疗服务

打造移动互联网医院&#xff0c;就是&#xff0c;通过移动互联网将医院与患者、医院内部&#xff08;医生、护士、领导层&#xff09;、医院与生态链上的各类组织机构连接起来。以患者为中心&#xff0c;优化医院业务流程&#xff0c;提升医疗服务质量与医院资源能效&#xff0…...

【建议收藏】免费体验的AI论文写作网站-「智元兔 AI」

在当今技术飞速发展的时代&#xff0c;越来越多的领域开始应用人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;。其中&#xff0c;AI写作工具备受瞩目&#xff0c;备受推崇。 在众多的选择中&#xff0c;智元兔AI是一款在笔者使用过程中非常有帮助…...

CUDA编程

线程全局索引计算方式 一维网格和一维的线程块 计算方法&#xff1a;...

gorilla/websocket的chat示例代码简单分析

代码地址&#xff1a;https://github.com/gorilla/websocket/tree/main/examples/chat 文件包含&#xff1a;main.go、hub.go、client.go、home.html main.go文件 func main() {flag.Parse()hub : newHub() // 实例化Hubgo hub.run() // 使用chan处理 增删Hub的连接 和 广播消…...

地图坐标展示工具folium

参考&#xff1a;https://github.com/python-visualization/folium https://zhuanlan.zhihu.com/p/384078185?utm_id0 https://www.w3cschool.cn/article/37568875.html 其他还有baidu&#xff1a;echarts 安装&#xff1a; pip install folium代码&#xff08;离线地图&a…...

Ruby 之方法委托

ruby 方法委托的优点在于&#xff0c;可以将多个不同实例&#xff08;或类&#xff09;的方法组织在一起&#xff0c;然后进行统一调用&#xff0c;方便各类方法的统一管理。比如下边示例中的 color 和 username&#xff0c;本来是不同类里边的方法&#xff0c;但最后都可以统一…...

[论文笔记]RetroMAE

引言 RetroMAE,中文题目为 通过掩码自编码器预训练面向检索的语言模型。 尽管现在已经在许多重要的自然语言处理任务上进行了预训练,但对于密集检索来说,仍然需要探索有效的预训练策略。 本篇工作,作者提出RetroMAE,一个新的基于掩码自编码器(Masked Auto-Encoder,MAE)…...

服务熔断保护实践--Sentinal

目录 概述 环境说明 步骤 Sentinel服务端 Sentinel客户端 依赖 在客户端配置sentinel参数 测试 保护规则设置 设置资源名 设置默认的熔断规则 RestTemplate的流控规则 Feign的流控规则 概述 微服务有很多互相调用的服务&#xff0c;构成一系列的调用链路&#xf…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...