Swift中的高阶函数:Filter,Map,Reduce,flatmap,compactMap
据我了解,高阶函数是将另一个函数/闭包作为参数并返回它的函数。
我将首先尝试解释这一点。 考虑以下代码,这将使您了解什么是高阶函数:
将函数传递给另一个函数:
前两个方法的类型为(Double,Double)->Double
。 第一个接受两个double
值并返回它们的和。 第二个返回这两个double
值的乘积。 第三种方法是接受三个参数的高阶函数。 两个double
值和一个类型为(Double,Double)->Double
的函数。 看一下方法调用。 您将了解高阶函数的工作原理。
从另一个函数返回函数:
在这里,函数doArithmeticOperation(isMultiply:)
是一个高阶函数,它返回类型为(Double,Double)->Double
的函数。
函数和闭包是迅速的一等成员。 它们可以保存到变量中并传递。
因此,此处,基于传递给doArithmeticOperation(isMultiply:)
函数的布尔值,它返回执行该操作的函数。
operationToPerform_1
是为您执行乘法的函数。
operationToPerform_2
是为您执行添加operationToPerform_2
的函数。
只需看一下函数定义和调用即可。 您将了解所有内容。
当然,您可以通过许多不同的方式来做同样的事情。 可能是您可以使用闭包代替函数。 您可以创建一个算术运算枚举并简化该函数。 我只是想解释什么是高阶函数。
这是swift中使用的一些高阶函数。 如果我正确理解,以下函数将闭包用作参数。 您可以使用这些函数对Swift集合类型(例如Array, set
或Dictionary
。
在进行以下操作之前,您应该了解什么是闭包。 阅读我关于闭包的文章
地图
使用 map
遍历一个集合,并对集合中的每个元素应用相同的操作。 map
函数返回一个数组,其中包含对每个项目应用映射或转换函数的结果。
映射数组:
假设我们有一个整数数组:
让arrayOfInt = [2,3,4,5,4,7,2]
如果我们必须将每个数字乘以10
怎么办? 我们通常使用for-in
循环遍历每个数字,操作正确吗?
var newArr:[Int] = [] 表示arrayOfInt中的值{newArr.append(value * 10)} print(newArr)//打印[20,30,40,50,40,70,20]
此代码看起来很冗长。 有一些样板代码,例如创建新的数组,可以使用map
来避免。 如果我们尝试映射Int
数组,则快速自动完成功能将显示以下内容。
闭包transform
接受一个int
参数并返回一个泛型类型。
让newArrUsingMap = arrayOfInt.map {$ 0 * 10}
这是在Int
数组上使用map
的最短版本。 我使用$运算符的closures
的简写语法。
以下所有代码的工作原理与上面相同,但是语法复杂到简化。 您应该在closures
方面具有丰富的知识来做到这一点。
map的工作: map
函数有一个自变量,它是一个闭包(一个函数),它在遍历集合时调用。 此闭包将集合中的元素作为参数并返回结果。 map函数以数组形式返回这些结果。
字典上的地图:
考虑一个以书名作为键,每本书的数量作为值的字典。
let bookAmount = [“ harrypotter”:100.0,“ junglebook”:100.0]
如果您尝试在字典上执行地图功能,则快速自动完成功能将如下所示:
在这里,对于上面的字典,当我们遍历集合时,闭包的参数分别是构成字典每个元素的键和值类型的String
和Double
。 返回类型可以是大写键数组,带折扣的values数组甚至是tuples数组 。 全取决于你。
注意 :映射函数的返回类型始终是通用数组。 您可以返回任何类型的数组。
现场地图:
在这种情况下,我们有一个包含Double
类型元素的集合,因此我们的闭包也需要一个Double
。 lengthInMeters
是一个集合。 lengthInFeet
是一个数组。
如果在将地图应用于集合时想知道集合的索引怎么办?
答案很简单。 在映射之前,您必须枚举它。
检查下面的代码。
设数字= [ 1、2、4、5 ] 设indexAndNum =数字.enumerated()。map {(index,element) 返回“ \(index):\(element)” } print(indexAndNum)// [“ 0:1”,“ 1:2”,“ 2:4”,“ 3:5”]
过滤
使用
filter
遍历一个集合并返回一个Array
,该Array
仅包含那些与include条件匹配的元素。
过滤阵列
考虑以下代码从integers
数组中过滤偶数。
现在,像map
一样,有一种简单的方法可以对集合类型进行过滤。
如果我们尝试对Int
数组使用filter
方法,则快速自动完成功能将显示以下内容。
如您所见,过滤器函数调用一个名为isIncluded
的闭包,该闭包以一个int
作为参数并返回一个Bool
。 因此, isIncluded
闭包将为每个集合项返回bool
值,并基于此结果将生成一个新的过滤数组。
filter
方法有一个用于指定包含条件的参数。 这是一个闭包,它以集合中的元素作为参数,并且必须返回一个Bool
指示是否应在结果中包含该项目。
像我们对地图所做的那样,可以进一步简化过滤器的闭合。
筛选字典
考虑一个以书名作为键,每本书的数量作为值的字典。
let bookAmount = [“ harrypotter”:100.0,“ junglebook”:1000.0]
如果您尝试对字典执行过滤功能,则快速自动完成功能将如下所示:
过滤器函数将通过传递每个键值对来调用名为isIncluded
的闭包,并进行条件检查(此处,它接受String
和Double
作为参数)。 最后,基于返回的布尔值,过滤器函数将决定是否在返回的数组中添加键值对。
重要提示: 字典上的过滤器函数返回一个 元组 数组, 如下面的游乐场代码所示:
这可以进一步简化为:
让FilterfilterOnDict = bookAmount.filter {$ 1> 100}
$ 0是键,$ 1是值
现场过滤
在这里,集合的过滤器闭包采用Double
参数,并为集合中的每个元素返回一个Bool
值。 基于此Bool
值,该项目将包含在过滤后的数组中。
重要说明: 返回类型是过滤后的数组。
降低
使用
reduce
可以合并集合中的所有项目以创建一个新值。
如苹果文档中的声明:
func reduce(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
因此, reduce
函数有两个参数。
- 一个是
initial value
,用于存储初始值或闭包从每次迭代返回的value
或result
。 - 另一个是带有两个参数的闭包,一个是
initial value
或闭包的上一次执行的result
,另一个是collection
的next item
。
减少阵列
让我们通过一个例子来理解这一点:
检查以下数字数组。
该reduce
函数将迭代四次。
- 初始值为0,x为0,y为1→返回x + y。 因此,初始值或结果变为1。
- 初始值或Result为1,x为1,y为2→返回x + y。 因此,初始值或Result变为3。
- 初始值或Result为3,x为3,y为3→返回x + y。 因此,初始值或结果变为6。
- 初始值或Result为6,x为6,y为4→返回x + y。 因此,初始值或结果变为10
reduce函数也可以简化为:
令let reduceNumberSum =数字.reduce(0){$ 0 + $ 1} print(reducedNumberSum)//打印10
这里的闭包是
(Int,Int)->Int
。 因此,我们可以传递(Int,Int)->Int
类型的任何函数或闭包。 在这种情况下,除了闭包之外,我们还可以传递基本的运算符函数,例如+,-,*,/。
令letedNumberSum =数字.reduce(0,+)//返回10
我们还可以在闭包内部使用乘法或其他运算或逻辑。
令letedNumberSum =数字.reduce(0){$ 0 * $ 1} // reduceNumberSum为0 ...
上一行也可以写成:
让reduceNumberSum =数字.reduce(0,*)
Reduce
也可以使用+
运算符连接字符串:
let code = [“” abc“,” def“,” ghi“] let text = codes.reduce(”“){$ 0 + $ 1} //结果为” abcdefghi“ 或 let text = codes.reduce(”“, +)//结果为“ abcdefghi”
减少字典
让我们减少bookAmount字典:
let bookAmount = [“ harrypotter”:100.0,“ junglebook”:1000.0]
对于字典,reduce函数内部的闭包采用两个参数。
- 该类型的初始值或结果值应减少为。
- 当前键值对的元组 。
可以使用短闭包语法进一步简化reducedBookNamesOnDict2
:
令letedBookNamesOnDict = bookAmount.reduce(“书籍为”){$ 0 + $ 1.key +“”} //或$ 0 + $ 1.0 +“”
减少现场
reduce on set的工作方式与数组相同。
返回类型是闭包的返回类型。 这是
Double
。
平面图
Flatmap用于展平集合的集合。 但是在展平集合之前,我们可以将map应用于每个元素。
苹果文档说 :返回一个数组,其中包含调用此序列的每个元素的给定转换的串联结果。
像这样阅读:map + (平集合)
在上面的(图2)中,flatMap遍历称为代码的集合中的所有集合。 在这里,各个集合是字符串(字符串是swift 4中的集合)。 步骤如下:
1。 将upperCased()
函数应用于所有字符串。 这类似于
[“ abc”,“ def”,“ ghi”]。map {$ 0.uppercased()}
输出将是:
输出:[“ ABC”,“ DEF”,“ GHI”]
2。 将所有子集合平整为一个集合。
输出:[“ A”,“ B”,“ C”,“ D”,“ E”,“ F”,“ G”,“ H”,“ I”]
小费:
在Swift 3中, flatmap也用于从collection中删除nil值 。 现在不推荐使用,编译器在使用时将发出警告。
我们应该改用compactMap 。 本文稍后会解释。
我认为,现在很清楚
flatMap
作用。
数组平面图
词典数组的平面图
平面映射后,它返回一个元组数组。 我们必须将其转换为数组:
平面图
平面图的优点:
让我们深入研究平面图的优势:
删除可选:
更有用的是,它了解可选内容,并将其从集合中删除。
平面图通过过滤或映射
我们可以将flatmap
应用于collection
的collections
。 即 array
arrays
将被展平为单个数组。 因此, flatmap
closure
接受一个参数集合,对此集合执行某些操作和/或返回该集合。 在这里,在返回集合之前,我们可以应用filter
或map
,甚至可以简化。
当应用短关闭语法时:
let onlyEven = collections.flatMap { $0.filter { $0 % 2 == 0 } }
链接:(地图+过滤器+缩小)
我们可以一个接一个地链接多个高阶函数。
您可以阅读有关链接方法的文章,以了解其工作原理。
链接背后的工作原理很简单。
map
和filter
方法作用于一个集合,并返回另一个集合。 现在,我们可以再次对该集合应用另一个更高阶的函数。 就这么简单。
假设我们要从数组数组中添加所有偶数的平方。
紧凑地图
返回一个数组,其中包含 对该序列的每个元素调用给定转换 的非零 结果。
如果对包含可选值的集合进行紧凑映射,则它将仅考虑non-nil
值。 这对集合和字典没有任何作用,因为集合不能有nil值,而对于字典,compactmap将提供具有键和值的元组数组。
注意:在swift 5中,引入了compactMapValues()
,它也为字典添加了功能。 即将更新文章。
小费:
map的输出仅由于数组中包含nil — value
而成为可选int ([ Int ?])
的集合。 否则它将是一个Int
数组。
结论
好,就这样! 您已掌握高阶功能!!
尽可能使用高阶函数:
- 它可以提高您的快速技能。
- 增强代码的可读性。
- 更多功能编程。
我们的目标是编写更少的代码,这更有意义!
请享用!!
参考: Link1
如果您喜欢阅读这篇文章,请分享并给予鼓掌,以便其他人可以找到它!
您可以在Medium上关注我以获取新文章。 另外,在LinkedIn上与我联系 和 推特
如果您有任何评论,问题或建议,请随时在下面的评论部分中发布它们!