Scala学习笔记12: 高阶函数

目录

    • 第十二章 高阶函数
      • 1- 作为值的函数
      • 2- 匿名函数
      • 3- 带函数参数的函数
      • 4- 参数类型推断
      • 5- 闭包
      • 6- SAM转换
      • 7- 柯里化
      • 8- 控制抽象
      • 9- return表达式
      • end

第十二章 高阶函数

在Scala中, 高阶函数 (Higher-Order Functions) 是指可以接受函数作为参数或返回函数作为结果的函数 ;

高阶函数是函数式编程的重要概念之一, 它允许在函数间传递行为, 从而实现更灵活和抽象的编程方式 .

1- 作为值的函数

在Scala中, 函数可以作为值进行传递和操作, 这是函数式编程的一个重要特性 ;

通过将函数赋值给变量或作为参数传递给其他函数, 可以方便地处理函数并实现更灵活的编程方式 ;

示例:

    // 定义一个接受函数作为参数的高阶函数def operateOnFunction(x: Int, func: Int => Int): Int = {func(x)}// 定义一个函数val double = (x: Int) => x * 2// 将函数赋值给变量val myFunction = double// 调用高阶函数,并将函数作为参数传递val result = operateOnFunction(5, myFunction)println(result) // 输出: 10
  • 在上面的示例中, 函数 double 被赋值给变量 myFunction , 然后 myFunction 作为值传递给 operateOnFunction 高阶函数进行操作 ;
  • 这展示了在Scala中如何处理函数作为值的灵活性 ;

通过将函数视为一等公民(First-Class Citizens) , Scala支持函数作为值的概念, 使得函数可以像其他数据类型一样被操作和传递, 从而实现更具变现力和可组合性的编程风格 .

2- 匿名函数

在Scala中, 匿名函数是一种没有明确名称的函数, 通常用于简单的功能或作为高阶函数的参数 ;

匿名函数可以通过 => 符号定义, 无需显示的命名函数

示例: 使用匿名函数作为高阶函数的参数

    // 定义一个接受函数作为参数的高阶函数def operateOnNumber(x: Int, func: (Int) => Int): Int = {func(x)}// 使用匿名函数作为参数val result1 = operateOnNumber(5, (x: Int) => x + 1) // 匿名函数实现加1操作val result2 = operateOnNumber(5, (x: Int) => x * x) // 匿名函数实现平方操作println(result1) // 输出: 6println(result2) // 输出: 25
  • 在上面的示例中, (x: Int) => x + 1)(x: Int) => x * x) 就是匿名函数, 他们分别实现了加1和平方的操作, 并为参数传递给 operateOnNumber 高阶函数 ;

通过使用匿名函数, 可以在不需要显示定义函数的情况下, 直接在需要的地方编写简单的功能逻辑, 从而简化代码并提高可读性 .

3- 带函数参数的函数

在Scala中, 可以创建接受函数作为参数的函数, 这种函数称为高阶函数 ;

通过高阶函数, 可以实现更灵活和抽象的编程方式 ;

示例:

    // 定义一个接受函数作为参数的高阶函数def operateOnList(list: List[Int], f: Int => Int): List[Int] = {list.map(f) // 使用map方法对列表中的每个元素应用函数f}// 定义一个函数,将每个元素乘以2def multiplyBy2(x: Int): Int = x * 2// 创建一个列表val numbers = List(1, 2, 3, 4, 5)// 使用高阶函数operateOnList,将每个元素乘以2val result = operateOnList(numbers, multiplyBy2)println(result) // 输出: List(2, 4, 6, 8, 10)
  • 在上面的示例中, operateOnList 是一个接受函数作为参数的高阶函数, 他接受一个整数列表和一个函数作为参数, 并使用传入的函数对列表中每个元素进行操作 ;
  • 通过将函数作为参数传递给高阶函数, 可以轻松地对列表中的元素进行不同的操作 .

4- 参数类型推断

在Scala中, 参数类型推断是指编译器根据上下文推断函数或表达式中参数的类型;

这种功能使得代码更简洁, 减少了显示类型声明的需要, 提高了代码的可读性和灵活性 ;

示例:

    // 定义一个函数, 编译器可以推断参数类型def add(x: Int, y: Int): Int = {x + y}// 调用函数, 无需显示声明参数类型val result = add(1, 2)println(result) // 输出: 3
  • 在这个示例中, 函数 add 的参数 xy 没有显示声明类型, 但编译器可以根据上下文推断出他们的类型为整数类型 ;
  • 这种参数类型推断功能使得Scala代码更具表现力和简洁性 ;

需要注意的是, 尽管Scala编译器通常能够进行类型推断, 但在某些情况下可能出现推断失败的情况, 此时可能需要显示声明参数类型以帮助编译器正确推断 .

5- 闭包

在Scala中, 闭包(Closure) 是指一个函数捕获并绑定了其自由变量的函数值 ;

换句话说, 闭包是一个函数, 它可以访问其定义范围之外的变量, 并且在函数被调用时仍然保持对这些变量的引用 ;

示例:

    // 定义一个接受整数参数的函数, 返回一个函数def multiplier(factor: Int): Int => Int = {(x: Int) => x * factor // 返回一个闭包函数, 该函数接受一个整数参数, 并返回一个整数}// 创建一个乘以3的闭包函数val multiplyBy3 = multiplier(3)// 使用闭包进行计算println(multiplyBy3(5)) // 输出: 15// 创建一个乘以4的闭包函数val multiplyBy4 = multiplier(4)// 使用闭包进行计算println(multiplyBy4(5)) // 输出: 20
  • 在上面的示例中, multiplier 函数返回一个闭包, 这个闭包捕获了 factor 变量的值 ;
  • 通过调用 multiplier 函数并传入不同的参数, 我们可以创建不同的闭包, 每个闭包都保持对其捕获的 factor 变量的引用 ;
  • 这使得闭包可以在函数定义范围之外访问和操作自由变量 ;

闭包在Scala中是一种强大的概念, 它可以帮助实现函数的状态保持和延迟计算等功能 .

6- SAM转换

在Scala中, SAM (Single Abstract Method) 转换是指将具有单个抽象方法的 trait 或抽象类转换为函数 ;

这种转换使得可以直接将符合抽象方法签名的函数传递给期望接收该 trait 或抽象类实例的地方, 从而简化代码并提高灵活性 ;

示例:

假设有一个定义了单个抽象方法的 trait MyTrait :

    trait MyTrait {def doSomething(x: Int): Int}

现在, 可以通过SAM转换将符合 doSomething 方法签名的函数转换为 MyTrait 的实例 ;

    // 定义一个函数, 符合MyTrait的抽象方法签名def myFunction: Int => Int = (x: Int) => x * 2// 使用SAM转换将函数转换为 MyTrait 实例val myTraitInstance = new MyTrait {override def doSomething(x: Int): Int = myFunction(x)}// 调用trait实例的抽象方法val result = myTraitInstance.doSomething(5)println(result) // 输出: 10
  • 在这个示例中, 我们定义了一个函数 myFunction , 它符合 MyTraitdoSomthing 方法的签名 ;
  • 然后通过 SAM 转换, 我们将这个函数转换为实现了 MyTrait 的匿名类实例 myTraitInstance , 并可以调用 doSomething 方法 ;

通过SAM转换, Scala提供了一种简洁的方式类将函数转换为 trait 或抽象类的实例, 从而更方便地在代码中使用函数式编程的特性 .

7- 柯里化

柯里化 (Currying) 是指将接受多个参数的函数转换为一系列接受单个参数的函数的过程 ;

在Scala中, 柯里化是一种常见的函数式编程技术, 它可以带来更高的灵活性和可组合性 ;

示例:

    // 定义一个接受两个参数的普通函数def add(a: Int, b: Int): Int = a + b// 使用柯里化将上述函数转换为接受单个参数的函数序列def curriedAdd(a: Int)(b: Int): Int = a + b // 使用柯里化语法定义的函数val addCurried = (a: Int) => (b: Int) => a + b // 使用的是匿名函数和函数字面量的方式实现柯里化// 调用柯里化后的函数序列val result1 = curriedAdd(2)(3)val result2 = addCurried(3)(5)println(result1) // 输出: 5println(result2) // 输出: 8
  • 在上面的示例中, add 函数接受两个参数, 而 curriedAddaddCurried 函数经过柯里化后, 将其转换为接受单个参数的函数序列 ;
  • 通过柯里化, 我们可以逐步传递参数, 每次传递一个参数, 最终得到函数的结果 ;

柯里化在Scala中常用于创建更具表现力和灵活性的函数, 特别是在函数式编程和组合函数时 ;

通过柯里化, 可以更方便地进行部分应用和函数组合, 使得代码更加简洁和易于理解 .

8- 控制抽象

在Scala中, 控制抽象 (Control Abstraction) 是一种编制技术, 通过它可以创建具有灵活控制结构的函数或方法 ;

控制抽象允许开发人员定义接受函数作为参数的高阶函数, 从而实现对代码执行流程的控制 ;

一个常见的控制抽象示例是使用高阶函数实现条件控制:

    // 定义一个高阶函数, 接受一个条件和两个函数作为参数def conditional(condition: Int => Boolean, f1: Int => Int, f2: Int => Int): Int => Int = {// 定义一个内部函数, 接受一个整数作为参数def inner(x: Int): Int = {// 如果条件成立, 则调用第一个函数, 否则调用第二个函数if (condition(x)) f1(x) else f2(x)}// 返回内部函数inner}val f1 = (x: Int) => x * 2val f2 = (x: Int) => x / 2val f3 = conditional((x: Int) => x % 2 == 0, f1, f2)println(f3(4)) // 输出: 8println(f3(5)) // 输出: 2
  • 在上面的示例中, conditional 函数是一个控制抽象, 它接受一个条件和两个函数作为参数 ;
  • 根据条件的结果, 它执行其中一个函数 ;
  • 通过这种方式, 我们可以灵活地控制代码的执行流程 ;

控制抽象在Scala中是一种强大的编程技术, 它使得代码更具表现力和灵活性 ;

通过使用控制抽象, 开发人员可以实现各种自定义控制结构, 从而更好地适应不同的编程需求 .

9- return表达式

在Scala中, return 表达式用于从函数或方法中提前返回结果 ;

然而, 在函数式编程中, 通常推荐避免使用 return , 而是通过函数的返回值来控制程序流程 ;

在Scala中, 可以使用 return 表达式, 但它的使用方式和在传统的命令式编程语言中略有不同 ;

示例:

    def findPositiveNumbers(numbers: List[Int]): Int = {for (num <- numbers) {if (num > 0) {return num // 找到第一个正数并返回, 使用return表达式提前返回结果}}return 0 // 如果没有找到正数, 返回0}val numbers = List(-1, 2, -3, 4, -5)val positiveNumber = findPositiveNumbers(numbers)println(positiveNumber) // 输出: 2
  • 在上面的示例中, findPositiveNumbers 函数用于在列表中找第一个正数 ;
  • 当找到第一个正数时, 使用 return 表达式提前返回结果 ;
  • 如果没有找到正数, 则返回默认值 0 ;

需要注意的是, 在函数式编程中, 通常更推荐使用不可变函数和纯函数, 避免副作用和可变状态 ;

因此, 尽量避免在Scala中过渡使用 return 表达式, 而是通过函数的返回值来控制程序流程, 以保持代码的函数式和易于理解性 .

end

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

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

相关文章

算法day26

第一题 429. N 叉树的层序遍历 本题的要求我们可以通过队列来辅助完成层序遍历&#xff1b; 如下图的n叉树&#xff1a; 步骤一&#xff1a; 我们定义一个队列&#xff0c;先进行根节点入队列操作&#xff1b; 步骤二&#xff1a; 我们进行当前队列每一个元素的出队列操作&…

嵌入式学习——Linux高级编程复习(UDP编程)——day43

1. UDP编程——函数接口 1.1 socket 1. 定义 int socket(int domain, int type, int protocol); 2. 功能 创建一个用来进程通信的套接字,返回文件描述符 3. 参数 domain:AF_INET IPv4协议族 type:SOCK_STREAM 流式套接字 tcp传输协议…

2024新消费特点---探索消费升级与品牌力量

最近看到不少消费视频&#xff0c;想起之前听过江南春的一场分享&#xff0c;结尾总结了张思维导图&#xff0c;分享给大家&#xff01; 随着时代的变迁&#xff0c;消费者的需求和市场环境也在不断演进。今天&#xff0c;我们将深入探讨消费升级的深层含义以及品牌如何在竞争…

集成学习 Ensemble Learning

目录 一、集成学习概览1、介绍2、学习器3、boosting和bagging比较1、样本选择2、样例权重3、预测函数4、计算5、其他 4、结合策略 二、Adaboost1、介绍2、运行过程3、特点4、代码示例 三、随机森林1、介绍2、随机森林生成3、特点4、优缺点5、代码示例6、参数介绍 四、GBDT1、介…

新火种AI|苹果终于迈进了AI时代,是创新还是救赎?

作者&#xff1a;一号 编辑&#xff1a;美美 苹果的AI战略&#xff0c;能够成为它的救命稻草吗&#xff1f; 苹果&#xff0c;始终以其独特的创新能力引领着行业的发展方向。在刚结束不久的2024年的全球开发者大会&#xff08;WWDC&#xff09;上&#xff0c;苹果再次证明了…

高考报志愿闲谈

当你的朋友在选择大学和专业时寻求建议&#xff0c;作为一名研究生并有高考经验的人&#xff0c;你可以提供一些有价值的见解和建议。 兴趣与职业目标&#xff1a; 首先询问他对哪些工科领域感兴趣&#xff0c;如机械工程、电子工程、计算机科学等。探讨他的职业目标。了解他将…

深入浅出 Qt 中 QListView 的设计思想,并掌握大规模、高性能列表的实现方法

在大规模列表控件的显示需求中&#xff0c;必须解决2个问题才能获得较好的性能&#xff1a; 第一就是数据存在哪里&#xff0c; 避免出现数据的副本。第二就是如何展示Item&#xff0c;如何复用或避免创建大量的Item控件。 在QListView体系里&#xff0c;QAbstractListModel解…

面试-NLP八股文

机器学习 交叉熵损失&#xff1a; L − ( y l o g ( y ^ ) ( 1 − y ) l o g ( 1 − ( y ^ ) ) L-(ylog(\hat{y}) (1-y)log(1-(\hat{y})) L−(ylog(y^​)(1−y)log(1−(y^​))均方误差&#xff1a; L 1 n ∑ i 1 n ( y i − y ^ i ) 2 L \frac{1}{n}\sum\limits_{i1}^{n}…

大模型学习之GLM结构

探索GLM&#xff1a;一种新型的通用语言模型预训练方法 随着人工智能技术的不断进步&#xff0c;自然语言处理&#xff08;NLP&#xff09;领域也迎来了革命性的发展。OpenAI的ChatGPT及其后续产品在全球范围内引起了广泛关注&#xff0c;展示了大型语言模型&#xff08;LLM&a…

分离式网络变压器与传统网络变压器在电路设计中如何选择?

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;今天分享的是&#xff1a;分离式网络变压器与传统网络变压器在电路设计中如何选择&#xff1f; 首先&#xff0c;我们要了解传统网络变压器和分离式网络变压器在设计上主要有以下不同点&#xff1a; 1、传统网络变…

java-权限修饰符

## Java中的权限修饰符 Java中的权限修饰符&#xff08;Access Modifiers&#xff09;用于控制类、方法和变量的访问级别。通过权限修饰符&#xff0c;可以在一定范围内保护类的成员&#xff0c;确保数据的封装和安全性。Java提供了四种主要的权限修饰符&#xff1a;private、…

​​Vitis HLS 学习笔记--添加 RTL 黑盒函数

目录 1. 简介 2. 用法详解 2.1 需要的文件 2.1.1 RTL 函数签名 2.1.2 黑盒 JSON 描述文件 2.1.3 RTL IP 文件 2.2 操作步骤 3. 总结 1. 简介 Vitis HLS 工具可以将现有的 Verilog RTL IP&#xff08;即硬件描述语言编写的模块&#xff09;集成到 C/C HLS 项目中。通过…

专家解读 | NIST网络安全框架(3):层级配置

NIST CSF在核心部分提供了六个类别的关键功能和子功能&#xff0c;并围绕CSF的使用提供了层级&#xff08;Tier&#xff09;和配置&#xff08;Profile&#xff09;两种工具&#xff0c;使不同组织和用户更方便有效地使用CSF&#xff0c;本文将深入探讨CSF层级和配置的主要内容…

【PL理论】(24) C- 语言:有块的作用域 | 更新的语法 | 新的语义域 | 环境 vs. 内存

&#x1f4ad; 写在前面&#xff1a;我们将再次扩展之前的C语言&#xff0c;让我们向这种语言引入“作用域”的概念。 目录 0x00 C- 语言&#xff1a;有块的作用域 0x01 C- 语言&#xff1a;更新的语法 0x02 新的语义域 0x03 环境 vs. 内存 0x00 C- 语言&#xff1a;有块的…

RDF 简介

RDF 简介 1. 引言 资源描述框架(Resource Description Framework,简称RDF)是一种用于描述网络资源的标准数据模型。它提供了一种通用的框架,用于表达关于资源的元数据,即数据的数据。RDF是语义网的核心技术之一,旨在使数据不仅能够被计算机处理,还能被计算机理解。在本…

图像去重技术:MD5哈希在自动化中的应用

目录 前言 一、MD5的介绍 二、常见的MD5哈希用途 三、hashlib库介绍 四、实际应用-图片去重 前言 MD5&#xff08;Message Digest Algorithm 5&#xff09;是一种广泛使用的哈希函数&#xff0c;它可以产生一个128位&#xff08;16字节&#xff09;的哈希值&#xff0c;通…

Golang | Leetcode Golang题解之第145题二叉树的后序遍历

题目&#xff1a; 题解&#xff1a; func reverse(a []int) {for i, n : 0, len(a); i < n/2; i {a[i], a[n-1-i] a[n-1-i], a[i]} }func postorderTraversal(root *TreeNode) (res []int) {addPath : func(node *TreeNode) {resSize : len(res)for ; node ! nil; node n…

大语言模型QA

Q:关于 Yi-9B 通过 input/output cosine 来分析模型,可能文档里没有把前提说明白。该指标确实存在你们提到的不同模型大小不可比的问题。所以我们比较的是同一个模型在不同训练阶段,以及 layer 深度相同的dense models 之间的比较。除了发现yi-6B/34B 随着训练 tokens 的增加…

11.NiO多线程优化

场景 单线程配合一个selector选择器管理多个channel上的事件。 问题 1.多核cpu,如果是单线程就会让cpu的力量被浪费。 2.单线程处理多个事件,如果某个事件耗费时间比较久,就会影响其它事件的处理。 例如:redis单线程写的,底层网络用的类似于nio和selector方式编写,所…

Polkadot <> Kusama 桥:打造无信任互操作性的开创性范例

原文&#xff1a;https://www.parity.io/blog/trustless-interoperability 作者&#xff1a;Adrian Catangiu&#xff5c;Rust 区块链核心工程师&#xff0c;Parity Technologies 编译&#xff1a;OneBlock Polkadot <> Kusama 桥是无信任互操作性的开创性范例。本文深…