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, setDictionary

在进行以下操作之前,您应该了解什么是闭包。 阅读我关于闭包的文章

地图

使用 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] 

如果您尝试在字典上执行地图功能,则快速自动完成功能将如下所示:

在这里,对于上面的字典,当我们遍历集合时,闭包的参数分别是构成字典每个元素的键和值类型的StringDouble 。 返回类型可以是大写键数组,带折扣的values数组甚至是tuples数组 。 全取决于你。

注意 :映射函数的返回类型始终是通用数组。 您可以返回任何类型的数组。

现场地图:

在这种情况下,我们有一个包含Double类型元素的集合,因此我们的闭包也需要一个DoublelengthInMeters是一个集合。 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的闭包,并进行条件检查(此处,它接受StringDouble作为参数)。 最后,基于返回的布尔值,过滤器函数将决定是否在返回的数组中添加键值对。

重要提示: 字典上的过滤器函数返回一个 元组 数组, 如下面的游乐场代码所示:

这可以进一步简化为:

  让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 ,用于存储初始值或闭包从每次迭代返回的valueresult
  • 另一个是带有两个参数的闭包,一个是initial value或闭包的上一次执行的result ,另一个是collectionnext item

减少阵列

让我们通过一个例子来理解这一点:

检查以下数字数组。

reduce函数将迭代四次。

  1. 初始值为0,x为0,y为1→返回x + y。 因此,初始值或结果变为1。
  2. 初始值或Result为1,x为1,y为2→返回x + y。 因此,初始值或Result变为3。
  3. 初始值或Result为3,x为3,y为3→返回x + y。 因此,初始值或结果变为6。
  4. 初始值或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函数内部的闭包采用两个参数。

  1. 该类型的初始值或结果值应减少为。
  2. 当前键值对的元组

可以使用短闭包语法进一步简化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应用于collectioncollections 。 即 array arrays将被展平为单个数组。 因此, flatmap closure接受一个参数集合,对此集合执行某些操作和/或返回该集合。 在这里,在返回集合之前,我们可以应用filtermap ,甚至可以简化。

当应用短关闭语法时:

 let onlyEven = collections.flatMap { $0.filter { $0 % 2 == 0 } } 

链接:(地图+过滤器+缩小)

我们可以一个接一个地链接多个高阶函数。

您可以阅读有关链接方法的文章,以了解其工作原理。

链接背后的工作原理很简单。 mapfilter方法作用于一个集合,并返回另一个集合。 现在,我们可以再次对该集合应用另一个更高阶的函数。 就这么简单。

假设我们要从数组数组中添加所有偶数的平方。

紧凑地图

返回一个数组,其中包含 对该序列的每个元素调用给定转换 的非零 结果。

如果对包含可选值的集合进行紧凑映射,则它将仅考虑non-nil值。 这对集合和字典没有任何作用,因为集合不能有nil值,而对于字典,compactmap将提供具有键和值的元组数组。

注意:在swift 5中,引入了compactMapValues() ,它也为字典添加了功能。 即将更新文章。

小费:

map的输出仅由于数组中包含nil — value而成为可选int ([ Int ?])的集合。 否则它将是一个Int数组。

结论

好,就这样! 您已掌握高阶功能!!

尽可能使用高阶函数:

  • 它可以提高您的快速技能。
  • 增强代码的可读性。
  • 更多功能编程。

我们的目标是编写更少的代码,这更有意义!

请享用!!

参考: Link1

如果您喜欢阅读这篇文章,请分享并给予鼓掌,以便其他人可以找到它!

您可以在Medium上关注我以获取新文章。 另外,在LinkedIn上与我联系 推特

如果您有任何评论,问题或建议,请随时在下面的评论部分中发布它们!

分类 雨燕 标签 过滤器 , 过滤器 , iOS , 地图 , 地图 , 缩小 , 迅速