Swift函数式编程——函数

目录

Swift函数式编程-函数

高阶函数(Higher order function)

一等函数(First class function)

闭包

函数柯里化(Function Curring)

函数式思维

使用函数解决问题

使用函数组合

总结


Swift函数式编程-函数

Swift支持函数式编程,这一篇介绍Swift中的函数。

高阶函数(Higher order function)

高阶函数,指可以将其他函数作为参数或者返回结果的函数。

Swift中的函数都是高阶函数,这和Scala,Haskell一致。与此对照的是,Java中没有高阶函数(Java 7支持闭包之前)。Java中方法没法单独存在,方法总是需要和类捆绑在一起。当你需要将一个函数传递作为参数给另外一个函数时,需要一个类作为载体来携带函数。这也是Java中监听器(Listener)的做法。

高阶函数对于函数式语言很重要,原因至少有两个:

  • 首先,高阶函数意味着您可以使用更高的抽象,因为它允许我们引入计算的通用方法。例如,可通过抽象出一个通用机制,遍历数组并向其中的每个元素应用一个(或多个)高阶函数。高阶函数可以被组合成为更多更复杂的高阶函数,来创造更深层的抽象。

  • 其次,通过支持函数作为返回值,就可支持构建动态性与适应性更高的系统。

 

一等函数(First class function)

一等函数,进一步扩展了函数的使用范围,使得函数成为语言中的“头等公民”。这意味函数可在任何其他语言结构(比如变量)出现的地方出现。一等函数是更严格的高阶函数。Swift中的函数都是一等函数。

闭包

闭包是一个会对它内部引用的所有变量进行隐式绑定的函数。也可以说,闭包是由函数和与其相关的引用环境组合而成的实体。

函数实际上是一种特殊的闭包,你可以使用{}来创建一个匿名闭包。使用 in 来分割参数和返回类型。

let r = 1...3
let t = r.map { (i: Int) -> Int inreturn i * 2
}

map函数遍历了数组,用闭包处理了所有元素。并返回了一个处理过的新数组。

Objective-C在后期加入了对闭包支持。闭包是一种一等函数。通过支持闭包,Objective-C拓展其语言表达能力。但是如果将Swift的闭包语法与Objective-C的闭包相比,Swift的闭包显得相当简洁和优雅,Objective-C的闭包则显得有些繁重复杂。

函数柯里化(Function Curring)

函数柯里化(Function Curring),是指接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,该函数返回一个接受余下参数的新函数。这个名词来源于逻辑学家 Haskell Curring。编程语言Haskell也取自这位逻辑学家的名字。

Haskell中函数都可以柯里化。在Haskell里的函数参数的型别声明也暗示了函数是柯里化的。Haskell中,返回值和参数之间,各个参数之间都是以->分隔。这是因为,如果你向可以接受多个参数的函数传入一个参数,函数仍然有返回值。它的返回值是另外一个函数。这个函数可以接受剩余的参数,我们称这个返回的函数为不全呼叫函数。本质上讲,Haskell的所有函数都只有一个参数。

下面语句在命令行中展示了Haskell里max的型别:

Prelude> :type max
max :: Ord a => a -> a -> a

其实也可以写作:

max :: (Ord a) => a -> (a -> a)

这意味着,如果向max传入一个参数a,将返回一个型别为(a -> a)的函数。

柯里化为构造新函数带来了方便。也免除了一些一次性的中间函数的编写工作。

Swift可以写出柯里化函数,虽然它还是保留了和Java类似的非柯里化函数的写法。以max函数为例,Swift中柯里化函数如下:

func max(a: Int)(b: Int) -> Int {return a > b ? a : b;
}let max3 = max(3)
max3(b: 5)

函数式思维

使用函数解决问题

一个简单的例子,找出1到10这个数组里的奇数。使用Java语言的思维(循环控制其实是过程式语言的思维),通常的写法会是这样:

var odds = [Int]()
for i in 1...10 {if i % 2 == 1 {odds.append(i)}
}println(odds)

输出结果为:[1, 3, 5, 7, 9]。而函数式的写法更为简单:

odds = Array(1...10).filter { $0 % 2 == 1 }
println(odds)

函数式的写法更为简单的原因是,放弃了对循环的控制,而使用函数处理序列。如何处理序列,即循环体里应该写的代码,在函数式编程中是由一个函数(通常会是闭包)传入。在计算机的底层对语言的实现中,仍然使用了循环控制这样的概念。但是,在编写函数式编程语言时,你并不需要这个概念。

另外一个简单的例子,如何找出1到10这个数组里的奇数,并且求它们的和呢?通常的写法会是这样:

var sumOdds = 0
var odds = [Int]()
for i in 1...10 {if i % 2 == 1 {odds.append(i)sumOdds += i}
}
println(sumOdds)

而函数式版本会是这样:

let sum = Array(1...10).myFilter { (i) in i % 2 == 1}.reduce(0) { (total, number) in total + number }
println(sum)

如果序列中的某些值做操作,过程式语言中,由于存在循环变量,就可以对循环所处的位置进行判断。而函数式编程语言的做法是使用函数构建一个符合条件的新序列,这里是Array(1...10).myFilter { (i) in i % 2 == 1},用于代表1到10里的奇数。然后再对新序列做进一步操作。这个例子中,使用reduce函数对新序列求和。

Haskell这种纯函数式编程语言,由于不需要,是没有循环控制语句的,你看不到for,while这样的关键字。但在Swift中,程序员在使用更高层级的抽象的同时意味着需要放弃对细节的控制。但是,这并不意味着无法在需要的时候回收控制。以函数式思维的一个重要方面是知道放弃多少控制,以及何时放弃。

使用函数组合

函数式编程思想中,面对复杂问题时,会使用一个个函数组合来为复杂问题建模。我们使用一个判断质数的例子来表现函数式编程的这一特点。我们会分别使用面向对象编程和函数式编程实现判断质数的算法,以对比两者的不同。

质数是因数只能是及其本身的整数。我们将使用这种算法:首先找出数字的因数,然后求所有因数的和,如果所有因数和为该数字加一,就可以确定该数字是质数。

为了先用面向对象的通常写法来实现该算法:

class PrimeNumberClassifier {let number: Intinit(number: Int){self.number = number}func isFactor(potential: Int) -> Bool {return number % potential == 0}func getFactors() -> [Int] {var factors : [Int] = Array<Int>()for it in 1...number {if isFactor(it) {factors.append(it)}}return factors}func sumFactors() -> Int {let factors = getFactors()var sum = 0for factor in factors {sum += factor}return sum}func isPrime() -> Bool {return self.sumFactors() == number + 1}
}

接着我们使用函数式写法:

func isFactor(number: Int)(potential: Int) -> Bool {return (number % potential) == 0
}func factors(number: Int) -> [Int] {let isFactorForNumber = isFactor(number)return Array(1...number).filter {isFactorForNumber(potential: $0)}
}func sumFactors(number: Int) -> Int {return factors(number).reduce(0){ (total, num) in total + num }
}func isPrime(number: Int) -> Bool {return sumFactors(number) == number + 1
}

可以看到,我们定义了四个函数,每个函数解决一个更小的问题。最后在isPrime为起点,把所有函数都串了起来,组成了整个算法实现。由于Swift中的函数都是一等函数。所以,我们可以使用filter和reduce这样接受闭包的函数提供对筛选和求和更简洁的表达方式。函数式写法中,所有的函数都是无状态的,无副作用的。也就是说无论你调用几次,只要函数的输入参数确定了,函数的输出就确定了。由于无状态,这里的每个函数都是易于复用的。你可以在任何外部模块放心地使用这些函数,而不用像在面向对象语言中那样担心对象的某个状态会对你调用的函数产生影响。

总结

函数式编程的核心是函数,函数是“头等公民”。这就像面向对象语言的主要抽象方法是类。Swift中的函数具有函数式语言中的函数的所有特点。这种支持使得你可以很容易地使用Swift写出函数式风格的代码。

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

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

相关文章

解决IDEA 不能正确识别系统环境变量的问题

问题描述 本人laptop 上的是设置了GOOGLE_APPLICATION_CREDENTIALS 这个环境变量的&#xff0c; 正常java or python 的程序能基于这个环境变量使用 某个gcp service account 去访问GCP的资源 [gatemanmanjaro-x13 ~]$ env | grep -i google GOOGLE_APPLICATION_CREDENTIALS/…

2023年,写博客带给我的收获与成长

文章目录 前言写博客的心路历程膜拜写博客大佬博客大佬带来的诱惑尝试写博客坚持写博客 决定写博客的原因2023年写博客的成就博客的创作粉丝的增长博客专家成就商务合作 2024年对技术写作的展望 前言 没错&#xff0c;我就是那个考试睡大觉、作文空白交卷的王二蛋。面对写作&a…

SpringBoot2.7-集成Knife4j

Knife4j 是什么 Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案 添加依赖 <!--引入Knife4j的官方start包,该指南选择Spring Boot版本<3.0,开发者需要注意--> <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knif…

Django Cookie和Session使用(十一)

一、Cookie Cookie具体指一小段信息&#xff0c;它是服务器发送出来存储在浏览器上的一组键值对&#xff0c;下次访问服务器时浏览器会自动携带这些键值对&#xff0c;以便服务器提取有用信息。 Cookie的特性 1、服务器让浏览器进行设置的 2、保存在浏览器本地&#xff0c;…

免费API-JSONPlaceholder使用手册

官方使用指南快速索引>>点这里 快速导览&#xff1a; 什么是JSONPlaceholder?有啥用?如何使用JSONPlaceholder? 关于“增”关于“改”关于“查”关于“删”关于“分页查”关于“根据ID查多个” 尝试自己搭一个&#xff1f;扩展的可能&#xff1f; 什么是JSONPlaceho…

面向对象(高级)知识点强势总结!!!

文章目录 一、知识点复习1-关键字&#xff1a;static1、知识点2、重点 2-单例模式&#xff08;或单子模式&#xff09;1、知识点2、重点 3-理解main()方法1、知识点2、重点 4-类的成员之四&#xff1a;代码块1、知识点2、重点 5-关键字&#xff1a;final1、知识点2、重点 6-关键…

新建虚拟环境并与Jupyter内核连接

第一步:在cmd里新建虚拟环境,shap38是新建的虚拟环境的名字 ,python=3.x conda create -n shap38 python=3.8第二步,安装ipykernel,打开anconda powershell prompt: 虚拟环境的文件夹位置,我的如图所示: 进入文件夹并复制地址: 输入复制的文件夹地址更改文件夹:…

交换域系数的选择:图像处理与编码的关键策略

在图像处理和编码领域&#xff0c;选择适当的交换域系数对于实现高效的图像处理和编码至关重要。交换域系数是指在特定的数学变换下产生的频域系数。通过选择合适的交换域系数&#xff0c;可以实现图像的压缩、增强和重构。本文将深入探讨交换域系数的选择在图像处理和编码中的…

你好!Apache Seata

北京时间 2023 年 10 月 29 日&#xff0c;分布式事务开源项目 Seata 正式通过 Apache 基金会的投票决议&#xff0c;以全票通过的优秀表现正式成为 Apache 孵化器项目&#xff01; 根据 Apache 基金会邮件列表显示&#xff0c;在包含 13 个约束性投票 (binding votes) 和 6 个…

Qt学习:Qt的意义安装Qt

Qt 的简介 QT 是一个跨平台的 C图形用户界面应用程序框架。它为程序开发者提供图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正地组件编程。 支持平台 xP 、 Vista、Win7、win8、win2008、win10Windows . Unix/Linux: Ubuntu 等…

【ARMv8M Cortex-M33 系列 2.1 -- Cortex-M33 使用 .hex 文件介绍】

文章目录 HEX 文件介绍英特尔十六进制文件格式记录类型hex 示例Cortex-M 系列hex 文件的使用 HEX 文件介绍 .hex 文件通常用于微控制器编程&#xff0c;包括 ARM Cortex-M 系列微控制器。这种文件格式是一种文本记录&#xff0c;用于在编程时传递二进制信息。.hex 文件格式最常…

docker学习笔记02-安装mysql

1.安装mysql8 下载MySQL镜像 docker pull mysql:8.0创建并启动容器 docker run -itd --name mysqltest -p 9999:3306 -e MYSQL_ROOT_PASSWORD123456 mysql其中-it是交互界面 -d是后台执行 -name 指定容器名称 -p指定映射端口 -e设置环境变量 最后mysql是镜像名或者用镜像id如…

Flask 日志

flask 日志 代码源码源自编程浪子flask点餐小程序代码 记录用户访问日志 和 错误日志 这段代码是一个基于Flask框架的日志服务类&#xff0c;用于 记录用户访问日志 和 错误日志。代码中定义了一个名为LogService的类&#xff0c;其中包含了两个静态方法&#xff1a;addAcc…

XUbuntu22.04之删除多余虚拟网卡和虚拟网桥(二百零四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

用Xshell连接虚拟机的Ubuntu20.04系统记录。虚拟机Ubuntu无法上网。本机能ping通虚拟机,反之不能。互ping不通

先别急着操作&#xff0c;看完再试。 如果是&#xff1a;本机能ping通虚拟机&#xff0c;反之不能。慢慢看到第8条。 如果是&#xff1a;虚拟机不能上网&#xff08;互ping不通&#xff09;&#xff0c;往下一直看。 系统是刚装的&#xff0c;安装步骤&#xff1a;VMware虚拟机…

【Linux】 last 命令使用

last 命令 用于检索和展示系统中用户的登录信息。它从/var/log/wtmp文件中读取记录&#xff0c;并将登录信息按时间顺序列出。 著者 Miquel van Smoorenburg 语法 last [-R] [-num] [ -n num ] [-adiox] [ -f file ] [name...] [tty...]last 命令 -Linux手册页 选项及作用…

vue项目表单使用正则过滤ip、手机号

import useFormValidate from /hooks/useFormValidatesetup(props, { emit }) {const { validateName, validateIPAndPort } useFormValidate()const state reactive({workFaceInfo: props.info?.id ? props.info : {},sysTypeData: props.sysType,formRules: {name: [{req…

SpringBoot 在IDEA中实现热部署 (JRebel实用版)

JRebel简介&#xff1a; JRebel是与应用程序服务器集成的JVM Java代理&#xff0c;可使用现有的类加载器重新加载类。只有更改的类会重新编译并立即重新加载到正在运行的应用程序中&#xff0c;JRebel特别不依赖任何IDE或开发工具&#xff08;除编译器外&#xff09;。但是&am…

『Linux升级路』冯诺依曼体系结构与操作系统

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;Linux &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、冯诺依曼体系结构 &#x1f4d2;1.1为什么要有体系结构 &#x1f4d2;1.2…

虚函数的讲解

文章目录 虚函数的声明与定义代码演示基类Person派生类Man派生类Woman 测试代码动态绑定静态绑定访问私有虚函数总结一下通过成员函数指针调用函数的方式 虚函数的声明与定义 虚函数存在于C的类、结构体等中&#xff0c;不能存在于全局函数中&#xff0c;只能作为成员函数存在…