【Scala---04】函数式编程 『 函数 vs 方法 | 函数至简原则 | 函数式编程』
文章目录
- 1. 函数 vs 方法
- 1.1 方法
- (1) 定义方法
- (2) 运算符即方法
- 1.2 函数
- (1) 定义函数
- (2) 匿名函数
- 1.3 方法转为函数
- 1.4 可变参数&默认参数
- 2. 函数至简原则
- 3. 函数式编程
- 3.1 函数式编程思想
- 3.3 函数柯里化&闭包
- 3.5 递归 & 尾递归
- 4. 补充
- 4.1 访问元祖元素
- 4.2 =>的含义
- 4.3 下划线的使用总结
1. 函数 vs 方法
在Java中方法与函数没区别,但是在Scala中方法和函数是不一样的。Java是面向对象的,Scala是面向函数式编程的,主要体现在如下4点:
Java | 对象可以作为一个值传递 | 对象可以作为参数传递 | 对象可以作为返回值传递 | 可以调用对象 |
---|---|---|---|---|
Scala | 函数可以作为一个值传递 | 函数可以作为参数传递 | 函数可以作为返回值传递 | 可以调用函数 |
Scala | 方法不可以作为一个值传递 | 方法不可以作为参数传递 | 方法不可以作为返回值传递 | 可以调用函数 |
可以看出Scala中函数比方法更加强大,而函数相比于方法最重要的功能就是函数能作为参数传递,也就是说a和b进行的操作不是写死的,而是可变的。
- 从位置上理解:方法 只能在类中定义,做为类的属性;函数 可以在任何地方定义。
- 从是否可以重载的角度:方法定义在类中可以实现重载;函数不可以重载。
- 从运行位置角度:方法是保存在方法区;函数是保存在堆中。
1.1 方法
(1) 定义方法
定义方法语法如下:
object 类名 {def 方法名([变量:变量类型,变量:变量类型]):返回值类型 = {方法体}
}
比如:
-
方法不能作为值传递
object Main {def main(args: Array[String]): Unit = {def add(x:Int, y:Int):Int = {x + y}val result = add // 会报错println(result)} }
-
方法不能作为参数传递
object Main {def main(args: Array[String]): Unit = {// 1. 定义calculate方法def calculate(x: Int, y: Int, func: (Int, Int) => Int): Int = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值func(x, y)}// 2. 定义add方法def add(x:Int, y:Int):Int = {x + y}// 3. 函数作为参数传递val result = calculate(2, 3, add) // 不会报错,自动转为函数传递println(result)} }
-
方法不能作为返回值传递
object Main {def main(args: Array[String]): Unit = {// 1. 定义getFunc方法def getFunc() = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值def func(x: Int, y: Int) = {x + y} func // 会报错}// 2. 得到函数println(getFunc())} }
-
方法可以直接调用
object Main {def main(args: Array[String]): Unit = {def add(x: Int, y: Int) = x + yprintln(add(2, 3)) // 输出:5} }
(2) 运算符即方法
- 所有的运算符本质上是方法。比如查看
Int
类
可以看到类中定义了很多以运算符命名的方法
因此,以下两种方法是等价的val a = 1 + 2val a = 1.+(2)
- 反过来,可以定义这样的方法,并调用
-
例子1
object Cal {def say(x:Int):Int = x }object Main {def main(args: Array[String]): Unit = {// 1. 用 类.方法名(参数列表) 形式调用方法var result1 = Cal.say(3)// 2. 用 类 方法 参数列表 形式调用方法var result2 = Cal say (3) var result3 = Cal say 3 // 如果参数列表只有一个值,括号是可以省略的} }
-
例子2
object Main {def say(x:Int):Int = xdef main(args: Array[String]): Unit = {// 1. 用 类.方法名(参数列表) 形式调用方法var result1 = this.say(3)// 2. 用 类 方法 参数列表 形式调用方法var result2 = this say (3) // 如果参数列表只有一个值,括号是可以省略的var result3 = this say 3} }
-
1.2 函数
(1) 定义函数
Scala中有23个函数接口类,函数的本质就是实现这些接口类来实现强大的功能:
Function0
:无参数,无返回值Function1
:…
定义函数语法如下:
object 类名 {def 函数名:([变量类型,变量类型])=>返回值类型 = ([变量:变量类型,变量:变量类型]) => {方法体}
}
比如:
-
函数可以作为一个值传递
object Main {def main(args: Array[String]): Unit = {val add = (x:Int, y:Int) => x + yval result = add // 不会报错println(result) } }
-
函数可以作为参数传递,此时需要指名函数的类型。格式为:
(参数类型)=>返回值类型
object Main {def main(args: Array[String]): Unit = {// 1. 定义calculate方法def calculate(x:Int, y:Int, func:(Int, Int)=>Int): Int = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值func(x, y)}// 2. 定义add函数def add = (x:Int, y:Int) => x + y// 3. 函数作为参数传递val result = calculate(2, 3, add)// 4. 输出println(result) // 输出5} }
-
函数可以作为返回值传递
object Main {def main(args: Array[String]): Unit = {// 1. 定义getFunc方法def getFunc() = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值val func = (x:Int, y:Int) => x + yfunc}// 2. 得到函数println(getFunc())// 3. 得到函数并执行函数println(getFunc()(2, 3)) // 输出5} }
-
函数可以执行
object Main {def main(args: Array[String]): Unit = {val add = (x:Int, y:Int) => x + yprintln(add(2, 3))} }
(2) 匿名函数
匿名函数与函数的语法区别就在于前者是val
关键字,后者是def
关键字。比如:
匿名函数简化了函数值传递:
注意:这只会定义一个函数,也不会执行函数。这点和Java不一样,java的lambda表达式是会创建对象,并执行构造方法。
1.3 方法转为函数
-
通用转换方式,在方法名后面 + 空格 + _ 即可,语法为:
方法名 _
object Main {def main(args: Array[String]): Unit = {def add(x:Int, y:Int):Int = {x + y}val result = add _ // 将方法转为函数println(result(2, 3)) // 输出:5} }
-
在参数处将方法转为函数时,可以简化,去掉
_
,直接方法名
object Main {def main(args: Array[String]): Unit = {// 1. 定义calculate方法def calculate(x: Int, y: Int, func: (Int, Int) => Int): Int = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值func(x, y)}// 2. 定义add函数def add(x:Int, y:Int):Int = {x + y}// 3. 函数作为参数传递val result1 = calculate(2, 3, add _) // 此时可优化,去掉 空格_val result2 = calculate(2, 3, add) // 优化后的代码} }
1.4 可变参数&默认参数
object Test03_FunArgs {def main(args: Array[String]): Unit = {// (1)可变参数:在类型后面加*号def sayHi(names:String*):Unit = {println(s"hi $names")}sayHi()sayHi("linhai")sayHi("linhai","jinlian")// (2)可变参数必须在参数列表的最后def sayHi1(sex: String,names:String*):Unit = {println(s"hi $names")}// (3)参数默认值def sayHi2(name:String = "linhai"):Unit = {println(s"hi ${name}")}sayHi2("linhai")sayHi2()// (4)默认值参数在使用的时候 可以不在最后def sayHi3( name:String = "linhai" , age:Int):Unit = {println(s"hi ${name}")}// (5)带名参数:指调用方法时,指定传参顺序sayHi3(age = 10, name = "niu")}
}
2. 函数至简原则
注意:方法最后一行的return可以省略,但是除此之外都不能省略。比如
object Test04_FuncSimply {def main(args: Array[String]): Unit = {//(1)return可以省略,Scala会使用方法体的最后一行代码作为返回值def func1(x: Int, y: Int): Int = {x + y}// (2)如果方法体只有一行代码,可以省略花括号def func2(x: Int, y: Int): Int = x + y//(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)// 此时,函数就变为了数学表达式的形式:f(x, y) = x + ydef func3(x: Int, y: Int) = x + y//(4)如果有return,则不能省略返回值类型,必须指定def func4(x: Int, y: Int): Int = {if (x < 20) {return x + y}2 * x + 3 * y}//(5)如果方法明确声明unit,那么即使函数体中使用return关键字也不起作用def func5(x: Int, y: Int): Unit = return x + y//(6)Scala如果期望是无返回值类型,可以省略等号def func6(x: Int, y: Int) {println(x + y)}// (7)如果方法无参,但是声明了参数列表,那么调用时,小括号,可加可不加def func7(): Unit = {println("hello")}// (8)如果方法没有参数列表,那么小括号可以省略,调用时小括号必须省略def func8 {println("hello")}// (9)如果不关心函数名,只关心映射逻辑,就会变为lambda表达式(x:Int, y:Int) => {println(x + y)}val func = (x:Int, y:Int) => {println(x + y)} // 如果要设置函数名可以这样。此时,func就是函数名}
}
3. 函数式编程
3.1 函数式编程思想
- 函数式编程思想:① 当做数学题,y = f(x),重要的是映射关系。② 使用val,在分布式上计算后不会产生歧义
- 通过前面可以知道,定义函数有3种形式:
- 方法转函数。先定义方法,再
方法名 _
- 直接定义函数
def
- 匿名函数
- 方法转函数。先定义方法,再
一看到
=>
或 一看到_
,就要想到这是表示函数。
3.3 函数柯里化&闭包
-
闭包:内层函数用到了外层函数变量,如果直接调用内层函数会取不到外层函数的这个变量值。此时,内层函数(万物皆对象,函数也是对象)的堆中的对象会保留一份引用到外层函数的值。
闭包参考链接 -
函数柯里化:将一个接收多个参数的函数转化成一个一个接受参数的函数过程,可以简单的理解为一种特殊的参数列表声明方式。函数柯里化
object TestFunction {val sum = (x: Int, y: Int, z: Int) => x + y + z// 函数柯里化的底层逻辑:本质是将函数作为返回值val sum1 = (x: Int) => {y: Int => { // 匿名函数z: Int => { // 匿名函数x + y + z}}}// 函数柯里化的另一种简单表达val sum2 = (x: Int) => (y: Int) => (z: Int) => x + y + z// 方法也有函数柯里化def sum3(x: Int)(y: Int)(z: Int) = x + y + zdef main(args: Array[String]): Unit = {sum(1, 2, 3)sum1(1)(2)(3) // sum1(1)调用完后,返回一个函数; sum1(1)(2)是调用返回的函数; .......sum2(1)(2)(3)sum3(1)(2)(3)} }
3.5 递归 & 尾递归
-
递归与Java中的递归一样:前面知道scala的方法返回值是可以省略的,默认分配返回值类型,但是 如果方法是递归方法,则必须指定方法的返回值类型,否则会报错。
object Test{def main(args: Array[String]): Unit = {// 实现阶乘def fact(n : Int) : Int = { // 必须指名方法的返回值类型// 跳出递归if(n == 1) return 1// 递归逻辑n * fact(n - 1)}// 调用阶乘方法println(fact(5))} }
-
尾递归:递归是将每次调用函数/方法会压入到栈中,是累计使用资源,容易造成栈溢出;而尾递归是覆盖使用资源,不会造成栈溢出。所以,尾递归资源利用率更加高。尾递归参考链接
一般支持函数式编程语言都支持尾递归;但是Java不支持尾递归。
4. 补充
4.1 访问元祖元素
变量名._数字
比如:x._1 表示访问x的第一个元素
4.2 =>的含义
https://blog.csdn.net/qq_43546676/article/details/130992479
4.3 下划线的使用总结
https://blog.csdn.net/qq_43546676/article/details/130874779
相关文章:

【Scala---04】函数式编程 『 函数 vs 方法 | 函数至简原则 | 函数式编程』
文章目录 1. 函数 vs 方法1.1 方法(1) 定义方法(2) 运算符即方法 1.2 函数(1) 定义函数(2) 匿名函数 1.3 方法转为函数1.4 可变参数&默认参数 2. 函数至简原则3. 函数式编程3.1 函数式编程思想3.3 函数柯里化&闭包3.5 递归 & 尾递归 4. 补充4.1 访问元祖元素4.2 &g…...

[华为OD] B卷 树状结构查询 200
题目: 通常使用多行的节点、父节点表示一棵树,比如 西安 陕西 陕西 中国 江西 中国 中国 亚洲 泰国 亚洲 输入一个节点之后,请打印出来树中他的所有下层节点 输入描述 第一行输入行数,下面是多行数据,每行以空…...

基于机器学习的学生学习行为自主评价设计与实现
管理员功能: a)学生学习数据管理:可查看学生学习的详情,编辑学生学习的内容,删除和添加学生学习,设置学生学习库存。 b)角色管理:增加删除学生用户,分配学生用户权限,查看学生用户…...
toml与json联系对比
前言 本文简单介绍toml;并且和json转化做对比,以及我对toml设计的理解。 参考: TOML: 简体中文 v1.0.0 json和toml转化工具 在线JSON转toml-toml转JSON - bejson在线工具 正文 数组 说白了,就是一个变量名,有多个…...

(已解决)org.springframework.amqp.rabbit.support.ListenerExecutionFailedException
报错截图 解决方案 1、登录rabbitMQ网址,删除所有队列 2、重启rabbitMQ 亲测有效!!!亲测有效!!!亲测有效!!!...

基于FPGA的数字信号处理(9)--定点数据的两种溢出处理模式:饱和(Saturate)和绕回(Wrap)
1、前言 在逻辑设计中,为了保证运算结果的正确性,常常需要对结果的位宽进行扩展。比如2个3bits的无符号数相加,只有将结果设定为4bits,才能保证结果一定是正确的。不然,某些情况如77 14(1110),如果结果只…...
基于STM32的宠物箱温度湿度监控系统毕业设计
基于STM32的宠物箱温度湿度监控系统毕业设计 一、项目背景与意义 随着人们生活水平的提高,养宠物已经成为一种流行趋势。然而,对于宠物的居住环境,尤其是温度与湿度的控制,是确保宠物健康的关键。本项目旨在设计一款基于STM32微…...

Linux sudo 指令
sudo命令 概念: sudo是linux下常用的允许普通用户使用超级用户权限的工具,允许系统管理员让普通用户执行一些或者全部的root命令,如halt,reboot,su等。这样不仅减少了root用户的登录和管理时间,同样也提高…...
【NumPy数组】:深入了解numpy.linspace()函数
一、numpy.linspace()函数的原理 numpy.linspace()函数的核心原理是在指定的起始值和终止值之间,按照给定的元素个数,生成等间隔的数值序列。与numpy.arange()函数不同,numpy.linspace()生成的是等间隔的数值,而不是等差的数值&a…...
计算机网络实验二:交换机的基本配置与操作
实验二:交换机的基本配置与操作 一、实验要求 (1)掌握windows网络参数的设置(TCP/IP协议的设置); (2)掌握交换机命令行各种操作模式的区别,以及模式之间的切换; (3)掌握交换机的全局的基本配置; (4)掌握交换机端口的常用配置参数; (5)查看交换机系统和…...

宏的优缺点?C++有哪些技术替代宏?(const)权限的平移、缩小
宏的优缺点? 优点: 1.增强代码的复用性。【减少冗余代码】 2.提高性能,提升代码运行效率。 缺点: 1.不方便调试宏。(因为预编译阶段进行了替换) 2.导致代码可读性差,可维护性差࿰…...

2024数维杯数学建模选题建议及各题思路来啦!
大家好呀,2024数维杯数学建模挑战赛开始了,来说一下初步的选题建议吧: 首先定下主基调, 本次数维杯建议选B。难度上C>A>B。B题目是比较经典的数据分析类题目,主要做统计分析差异显著性以及相关…...
centos的常用命令
CentOS是一个基于Red Hat Enterprise Linux(RHEL)的开源操作系统,常用于服务器环境。以下是一些CentOS中常用的命令: 文件和目录管理: ls:列出目录中的文件。 ls -l:以长格式列出文件和目录的…...
【Android】使用Handler实现一个定时器
需求 实现一个定时任务,每隔一秒执行一次 实现 使用Handler实现 private Handler topUIHandler;private void initTopUiHandler() {topUIHandler new Handler(getMainLooper()) {Overridepublic void handleMessage(Message msg) {//执行这个定时任务updateTop…...

Java | Leetcode Java题解之第80题删除有序数组中的重复项II
题目: 题解: class Solution {public int removeDuplicates(int[] nums) {int n nums.length;if (n < 2) {return n;}int slow 2, fast 2;while (fast < n) {if (nums[slow - 2] ! nums[fast]) {nums[slow] nums[fast];slow;}fast;}return sl…...

java后端15问!
前言 最近一位粉丝去面试一个中厂,Java后端。他说,好几道题答不上来,于是我帮忙整理了一波答案 G1收集器JVM内存划分对象进入老年代标志你在项目中用到的是哪种收集器,怎么调优的new对象的内存分布局部变量的内存分布Synchroniz…...

OmniPlan Pro 4 for Mac中文激活版:项目管理的新选择
OmniPlan Pro 4 for Mac作为一款专为Mac用户设计的项目管理软件,为用户提供了全新的项目管理体验。其直观易用的界面和强大的功能特性,使用户能够轻松上手并快速掌握项目管理要点。 首先,OmniPlan Pro 4 for Mac支持自定义视图,用…...

二叉树的广度优先遍历 - 华为OD统一考试(D卷)
OD统一考试(D卷) 分值: 200分 题解: Java / Python / C++ 题目描述 有一棵二叉树,每个节点由一个大写字母标识(最多26个节点)。 现有两组字母,分别表示后序遍历(左孩子->右孩子->父节点)和中序遍历(左孩子->父节点->右孩子)的结果,请输出层次遍历的结…...

代码随想录-算法训练营day31【贪心算法01:理论基础、分发饼干、摆动序列、最大子序和】
代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第八章 贪心算法 part01● 理论基础 ● 455.分发饼干 ● 376. 摆动序列 ● 53. 最大子序和 贪心算法其实就是没有什么规律可言,所以大家了解贪心算法 就了解它没有规律的本质就够了。 不用花心思去研究其…...

如何使用Transformer-TTS语音合成模型
1、技术原理及架构图 Transformer-TTS主要通过将Transformer模型与Tacotron2系统结合来实现文本到语音的转换。在这种结构中,原始的Transformer模型在输入阶段和输出阶段进行了适当的修改,以更好地处理语音数据。具体来说,Transformer-TT…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...