HOF钻孔,高阶功能
HOF钻孔,高阶功能
哦是的!!! 您完全看到了。 钻孔:不是“打个洞”,而是军事版本的训练。 在这里… mobidevtalk.com
哦是的!!! 您完全看到了。 钻孔:不是“打个洞”,而是军事训练。 在这两个博客文章系列中,我们将深入探讨Higher order Function
aka HOF
。 所以收紧靴子,会很泥泞。
如果您是可以使用HOF的人或女孩; 嗯,我猜😉。
但是您对幕后发生的事情很感兴趣,然后您就处于适当的位置。
我们将HOF演练分为两部分。
内容:
- 第一部分,意味着此博客文章,将详细介绍HOF。
- 在第二部分,我们将生成一个复杂的案例研究,我们将使用HOF进行解决。 这样一来,我们最终可以掌握HOF。
背景:
- 真正的HOF是什么!
- 为什么要使用
- 预先知识
- Swift的一些HOF
你好,我是HOF
尽管HOF
这个名称似乎很难理解,但实际上并非如此。 HOF唯一困难的事情是对何时使用哪种HOF的理解。
HOF
是一个非常简单的概念。 HOF
, Higher Order Function
,基本上是具有以下两个属性的函数:
- 带/不带其他参数的闭包作为参数
- 返回一个函数或一些累计值
简而言之, HOF
将有助于重复 操作 。 现在,这两个关键字repetitive
和operation
意味着什么?
关键字repetitive
; 你已经猜对了吧? 是的,它仅对集合类型有效,例如Array
Dictionary
Set
。 集合的每个元素都将包括在内。
其次, operation
基本上是数学运算。 我们可以将operation
定义为f(x)
。 因此, HOF
一个简单示例可以归结为以下内容:
是的,就是这样。 是不是很简单?
我敢肯定,这个简单的概念已经分解,现在让您认为HOF
毕竟不是UFO
。 😏
为什么我们关心HOF
- 并非是外星人,而是一些聪明的软件工程师 。 因为那些已经在使用
HOF
并希望其他人知识渊博或至少有意愿知道的人。 - 产生良好的精确干净代码。
- 功能编程。
使用HOF之前需要知道的事情
- HOF仅适用于Collection类型,即
Array
,Dictionary
和Set
。 - 使用HOF之前,我们必须知道的最重要的事情是闭包。
旁注:函数是特殊类型的闭包。
我们将介绍封闭的一些属性和格式,以促进HOF研究。
HOF所需的关闭属性:
- 可以推断参数类型并从环绕声上下文返回
- 对于单表达式闭包,不需要
return
关键字 - 速记参数名称
闭包表达
{ (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-nil
和nil
实例,可选类型,在这种情况下为[String?]
。
因此,我们可以将compactMap
视为具有内置nil
过滤器的map
函数。
何时使用compactMap :
当需要对集合执行公共操作时,是否可以选择是否键入,并且最终结果需要键入集合。
对于可失败的failable initializer
, compactMap
极其有用,在init
可能会失败并返回nil实例的情况下。 在以后的文章中,我们将介绍failable initializer
。
["40", "unknown", "28"].compactMap({ Int($0) })
//[40, 28]
您始终可以参考关闭详细信息进行说明。
我们可以从文档中找到Int
的failable 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
是通用实例。
另一方面, Element
是associatedtype
实例,它代表相应集合的元素。 同样,我们将来还会有一些associatedtype
。
并且throws
和rethrows
用于异常处理。 如果您有兴趣,请访问我们的错误处理博客文章。
因此,在接下来的部分中,我们将忘记Result
, Element
, throw
和rethrow
。
现在,我们将通用类型替换为我们众所周知的类型,例如Int
和Double
,我们的输入将是一个Int
数组,并且累积结果将是Double
。 现在, reduce
至少第一次变得更容易消化。
func reduce(_ initialResult: Double, _ nextPartialResult: (Double, Int) throws -> Double) rethrows -> Double
眼睛有点容易。 让我们使其更容易。 现在让我们删除throw
和throw
,只是为了理解!
func reduce(_ initialResult: Double, _ nextPartialResult: (Double, Int) -> Double) -> Double
现在让我们将reduce
到均匀的水平。 基本上,我们现在可以在此方法签名上找到这三个部分。
-
initialResult
是Double
类型。 -
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
的第一个区别是Result
的inout
版本。 那么inout
是什么呢? 当一个参数被标记为inout
,那就意味着我们可以覆盖该参数。
第二个是闭包updateAccumulatingResult
,它不返回,而是将结果分配给Result
类型的实例。
而且显然方法签名在两者之间有些不同,这个具有。
而且我知道我们可以像上一个版本一样分解此版本。 加油并把它分解。 相信我,这是值得的。
因此,让我们用reduce
的inout
版本更新数字总和
numbers.reduce(into: initialResult) { (result, num) in
result += Double(num)
}
// 8.9
啊哈突然变短了。 看一下以前版本的更改。
是的,你得到的是result
的inout
版本。 正如我们可以在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交谈。