HOF钻孔,高阶功能

HOF钻孔,高阶功能
哦是的!!! 您完全看到了。 钻孔:不是“打个洞”,而是军事版本的训练。 在这里… mobidevtalk.com

哦是的!!! 您完全看到了。 钻孔:不是“打个洞”,而是军事训练。 在这两个博客文章系列中,我们将深入探讨Higher order Function aka HOF 。 所以收紧靴子,会很泥泞。

如果您是可以使用HOF的人或女孩; 嗯,我猜😉。
但是您对幕后发生的事情很感兴趣,然后您就处于适当的位置。

我们将HOF演练分为两部分。

内容:

  • 第一部分,意味着此博客文章,将详细介绍HOF。
  • 在第二部分,我们将生成一个复杂的案例研究,我们将使用HOF进行解决。 这样一来,我们最终可以掌握HOF。

背景:

  • 真正的HOF是什么!
  • 为什么要使用
  • 预先知识
  • Swift的一些HOF

你好,我是HOF

尽管HOF这个名称似乎很难理解,但实际上并非如此。 HOF唯一困难的事情是对何时使用哪种HOF的理解。
HOF是一个非常简单的概念。 HOFHigher Order Function ,基本上是具有以下两个属性的函数:

  • 带/不带其他参数的闭包作为参数
  • 返回一个函数或一些累计值

简而言之, HOF将有助于重复 操作 。 现在,这两个关键字repetitiveoperation意味着什么?
关键字repetitive ; 你已经猜对了吧? 是的,它仅对集合类型有效,例如Array Dictionary Set 。 集合的每个元素都将包括在内。
其次, operation基本上是数学运算。 我们可以将operation定义为f(x) 。 因此, HOF一个简单示例可以归结为以下内容:

是的,就是这样。 是不是很简单?
我敢肯定,这个简单的概念已经分解,现在让您认为HOF毕竟不是UFO 。 😏

为什么我们关心HOF

  • 并非是外星人,而是一些聪明的软件工程师 。 因为那些已经在使用HOF并希望其他人知识渊博或至少有意愿知道的人。
  • 产生良好的精确干净代码。
  • 功能编程。

使用HOF之前需要知道的事情

  • HOF仅适用于Collection类型,即ArrayDictionarySet
  • 使用HOF之前,我们必须知道的最重要的事情是闭包。
    旁注:函数是特殊类型的闭包。

我们将介绍封闭的一些属性和格式,以促进HOF研究。

HOF所需的关闭属性:

  1. 可以推断参数类型并从环绕声上下文返回
  2. 对于单表达式闭包,不需要return关键字
  3. 速记参数名称

闭包表达

 { (params) -> return-type in 
//Code
}

对于本节的关闭,我们将使用以下绝地数组。

 let jedi = ["Yoda", "Obi-Wan Kenobi", "Anakin Skywalker", "Luke Skywalker"] 

常规闭包 ,参数类型均为String ,返回Bool

 jedi.sorted { (firstJedi: String, secondJedi: String) -> Bool in 
return firstJedi < secondJedi
}

推断参数和返回类型:

 jedi.sorted { (firstJedi, secondJedi) in 
return firstJedi < secondJedi
}

删除return

 jedi.sorted { (firstJedi, secondJedi) in 
firstJedi < secondJedi
}

速记参数名称

Swift为内联闭包提供了简写参数名称 。 闭包的参数/参数的值可以称为$0 $1 $2 ,依此类推。

 jedi.sorted(by: { $0 < $1 }) 

现在开始,我们将尽可能使用速记方式。 当我们对HOF的关闭感到困惑时,我们总是可以回到上面的讨论。

斯威夫特你有什么HOF?

在本节中,我们将讨论Swift上一些流行的HOF。

  • 地图
  • compactMap
  • 过滤
  • 降低

地图:

map用于对集合的所有元素执行通用操作。

 [1, 2, 3].map({ $0 + 5 }) 
//[6, 7, 8]

如果对封闭有任何困惑,我们总是可以重新查看HOF所需的封闭属性。

何时使用 地图
需要对集合执行常见操作。

compactMap:

让我们考虑以下示例,并从那里说明compactMap的使用:

 let jedi = [nil, "Obi-Wan Kenobi", "Anakin Skywalker", "Luke Skywalker"] 
print( jedi.map({ "Name: \($0)" }) )
//[nil, Optional("Obi-Wan Kenobi"), Optional("Anakin Skywalker"), Optional("Luke Skywalker")]
 print( jedi.compactMap({ $0 })) 
//["Obi-Wan Kenobi", "Anakin Skywalker", "Luke Skywalker"]

如您所见,其简单的compactMap将返回non-nil compactMap实例(类型为instance)的数组,在这种情况下为[String] 。 而map将返回non-nilnil实例,可选类型,在这种情况下为[String?]

因此,我们可以将compactMap视为具有内置nil过滤器的map函数。

何时使用compactMap
当需要对集合执行公共操作时,是否可以选择是否键入,并且最终结果需要键入集合。

对于可失败的failable initializercompactMap极其有用,在init可能会失败并返回nil实例的情况下。 在以后的文章中,我们将介绍failable initializer

 ["40", "unknown", "28"].compactMap({ Int($0) }) 
//[40, 28]

您始终可以参考关闭详细信息进行说明。

我们可以从文档中找到Intfailable initializer 。 声明是convenience init?(_ description: String) 。 再次,我们将讨论convenience init? 在将来的帖子上。

因此,通过使用compactMap我们可以消除nil个值,并获得Int的适当实例。

flatMap现在已弃用。 我们需要使用compactMap而不是flatMap

过滤:

顾名思义, filter HOF将过滤集合。 我们需要给filter添加一个封闭物,该封闭物将充当过滤器约束。
如果简单地说, filter将对具有某些过滤操作的集合进行操作,最后将为我们提供过滤后的集合。
让我们来看一个例子。

 let jedi = ["Yoda", "Obi-Wan Kenobi", "Anakin Skywalker", "Luke Skywalker"] 
 jedi.filter({ $0.contains(" ") }) 
// ["Obi-Wan Kenobi", "Anakin Skywalker", "Luke Skywalker"]

$0是简写参数名称, contains()String的常规闭包/函数。
在上面的示例中,我们正在过滤不包含由空格分隔的两部分的名称。 因此,最后我们得到了两部分绝地名字的集合。

让我们来看另一个例子。

 [20, 23, 21, 5, 0, 45].filter({ $0 % 5 == 0 }) 
//[20, 5, 0, 45]

在这里,我们对Int数组进行过滤,就好像5的提醒等于零。

何时使用过滤器
每当我们需要过滤一个集合时。

降低:

reduce的功能是将集合的元素组合为累积收益。
将多种元素简化为一种的一种。
在我们这里讨论的HOF中, reduce将是最困难的一项。 哦,别紧张,我们把它掩盖了。 👌

让我们以一些简单的示例和更长的闭包格式开始,以便首先获得它。 然后,我们将逐步转向最佳格式。

所以这是reduce签名。

 func reduce(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result 

Result是一个通用类型实例,我们将在以后的文章中进行介绍。 但就目前而言,通用类型表示一组类型,这些类型可能受一个或多个协议限制。
第一个括号开头的告诉编译器Result是通用实例。

另一方面, Elementassociatedtype实例,它代表相应集合的元素。 同样,我们将来还会有一些associatedtype

并且throwsrethrows用于异常处理。 如果您有兴趣,请访问我们的错误处理博客文章。

因此,在接下来的部分中,我们将忘记ResultElementthrowrethrow

现在,我们将通用类型替换为我们众所周知的类型,例如IntDouble ,我们的输入将是一个Int数组,并且累积结果将是Double 。 现在, reduce至少第一次变得更容易消化。

 func reduce(_ initialResult: Double, _ nextPartialResult: (Double, Int) throws -> Double) rethrows -> Double 

眼睛有点容易。 让我们使其更容易。 现在让我们删除throwthrow ,只是为了理解!

 func reduce(_ initialResult: Double, _ nextPartialResult: (Double, Int) -> Double) -> Double 

现在让我们将reduce到均匀的水平。 基本上,我们现在可以在此方法签名上找到这三个部分。

  • initialResultDouble类型。
  • nextPartialResult是一个闭包,签名为(Double, Int) -> Double 。 因此,这意味着第一个参数是Double类型,第二个参数是Int类型,闭包的返回值是Double类型。
  • 最后一部分是, reduce的返回值是Double类型。

🤓

现在事情很容易。 那么现在呢。 是的必须是一个例子。 让我们从简单的一开始。

 let numbers = [0, 1, 2, 4] 
let initialResult = 1.9
 numbers.reduce( initialResult, { (result: Double, num: Int) -> Double in 
var total = result
(total += Double(num))
return total
})
// 8.9

请记住,当我们说闭包时我们说闭包很聪明,可以推断参数的类型并从环绕上下文中返回。 因此,通过删除类型,下一个版本将是:

 numbers.reduce( initialResult, { (result, num) in 
var total = result
(total += Double(num))
return total
})
//8.9

闭包也可以具有简写形式。 所以:

 numbers.reduce(initialResult, { $0 + Double($1) }) 
// 8.9

哎呀,聪明。 🕶️

图片图片图片图片! 给我一些图像…

现在还有另一个reduce版本。 看起来几乎一样。

 func reduce(into initialResult: Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result 

先前版本的reduce和当前版本的reduce的第一个区别是Resultinout版本。 那么inout是什么呢? 当一个参数被标记为inout ,那就意味着我们可以覆盖该参数。
第二个是闭包updateAccumulatingResult ,它不返回,而是将结果分配给Result类型的实例。
而且显然方法签名在两者之间有些不同,这个具有。

而且我知道我们可以像上一个版本一样分解此版本。 加油并把它分解。 相信我,这是值得的。

因此,让我们用reduceinout版本更新数字总和

 numbers.reduce(into: initialResult) { (result, num) in 
result += Double(num)
}
// 8.9

啊哈突然变短了。 看一下以前版本的更改。
是的,你得到的是resultinout版本。 正如我们可以在result写的那样,因此我们直接使用addition assignment operator, +=来更新结果。 出于同样的原因, inout ,我们没有从闭包中返回任何result

好的,我们不会忘记闭包的简写形式,对吗? 这里是:

 numbers.reduce(into: initialResult, { $0 += Double($1) }) 

所以现在一切都清楚了。

何时使用 reduce
当我们需要一个集合的累积结果时。

我们知道reduce HOF是如何工作的。 但是我们忘记了什么吗? 来…绝地

 let jedi = ["Yoda", "Obi-Wan Kenobi", "Anakin Skywalker", "Luke Skywalker"] 
jedi.reduce(into: "Available Jedi: ", { $0 += "\n \($1)" })
/*
Available Jedi:
Yoda
Obi-Wan Kenobi
Anakin Skywalker
Luke Skywalker
*/

现在完成了。 ⌛

只要记住当您对HOF感到困惑时,就去分解一下HOF。 然后提出较长版本的闭包,然后逐渐移至较短的闭包格式。

下篇文章很快见。 直到Happy Mobi Dev交谈。