swift 泛型
2016-05-07 16:29
344 查看
/**
// 泛型
接受其它函数作为参数的函数有时被称为高阶函数。
Swift 标准库
中作用于数组的高阶函数中漫游。Swift
泛型,展示如复杂计算运用于数组
*/
/*
顶层函数和扩展
写一个函数,它接受一个给定的整型数组,通过计算得到并返回一个新数组,新
数组各项为原数组中对应的整型数据加一。这一切,仅仅只需要使用一个 for
循环就能非常容 易地实现
*/
func incrementArray(xs:[Int]) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(x +
1) }
return result
}
/**
*/
func doubleArray1(xs:[Int]) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(x *
2) }
return result
}
var tmpArray = doubleArray1([1,2,3,4])
/// 两个函数有大量相同的代码,我们能不能将没有区别的地方抽象出来,并单独写一个体现这
// 种模式且更通用的函数
func computeIntArray(xs: [Int], transform:
Int -> Int ) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(transform(x))
}
return result
}
/**
* 现在,取决于我们想如何根据原数组得到一个新数组,我们可以向函数传递不同的参数。
doubleArray 函数和 incrementArray
函数都精简为了一行调用 computeIntArray
的语句:
*/
func doubleArray2(xs:[Int]) -> [Int] {
return
computeIntArray(xs) {
x in x * 2
}
}
func computeBoolArray(xs: [Int], transform:
Int -> Bool) -> [Bool] {
var result: [Bool] = []
for x in xs {
result . append(transform(x))
}
return result
}
func isEvenArray(xs: [Int ]) -> [Bool] {
return computeBoolArray(xs) { x
in x % 2 ==
0}
}
/**
* 这个方案的扩展性并不好。如果接下来需要计算 String
类型呢?
是否还需要定义另
一个高阶函数来接受 Int -> String
类型的参数
解决方案:使用泛型
genericComputeArray<T>
理解为一个函数族,类型参数 T
的每个选择都会确定一个新函数
该函数接受一个整型数组和一个 Int -> T类型的函数作为参数,并返回一个[T]类型的数组
*/
func genericComputeArray<T>(xs: [Int], transform:
Int -> T) -> [T] {
var result: [T] = []
for x in xs {
result.append(transform(x))
}
return result
}
/**
进一步将这个函数一般化。没有理由让它仅能对类型为 [Int]
的输入数组进行处理
将数组类型进行抽象,能得到下面这样的类型签名
写了一个 map
函数,它在两个维度都是通用的:对于任何 Element
的数组和 transform: Element -> T
函数,它都会生成一个 T
的新数组。
这个 map
函数甚至比我们之前 看到的 genericComputeArray
函数更通用。
*/
func map<Element,T>(xs:[Element],transform:Element ->T)->[T]{
var result :[T] = []
for x in xs{
result.append(transform(x))
}
return result
}
/**
* 事实上,可以通过 map
来定义 genericComputeArray:
*/
func genericComputeArray2<T>(xs: [Int], transform:
Int -> T) -> [T] {
return map(xs, transform: transform)
}
// 按照 Swift
的惯例将 map 定义为 Array
的扩展会更合
适
extension Array {
func map<T>(transform:
Element -> T) -> [T] {
var result: [T] = []
for x in
self {
result . append(transform(x)) }
return result
}
}
/**
map(xs, transform) 的替代,我们现在可以通过 xs.map(transform)
来调用 Array的map
函数
定义 map
函数,它已经是 Swift
标准库的
一部分了(实际上,它基于 SequenceType
协议被定义)。
map 的定义中并
没有什么复杂难懂的魔法 ,能够轻松地自己定义它
*/
func genericComputeArray_<T>(xs: [Int], transform:
Int -> T) -> [T] {
return xs.map(transform)
}
var mapArray =
genericComputeArray_(tmpArray) {
x in x
}
// Filter
let exampleFiles = ["README.md",
"HelloWorld.swift", "FlappyBird.swift"]
func getSwiftFiles( files: [String]) -> [String] {
var result: [String] = []
for file in files {
if file .hasSuffix(".md") {
result . append(file)
}
}
return result
}
getSwiftFiles(exampleFiles)
// MARK: - 想查找没有扩展名的所有文
件,或者是名字以字符串 "Hello"
开头的文件
// 为了进行一个这样的查找,我们可以定义一个名为 filter
的通用型函数。就像之前看到的 map
那样, lter 函数接受一个函数作为参数。 filter
函数的类型是 Element -> Bool ——
对于数 组中的所有元素,此函数都会判定它是否应该被包含在结果中
// 就像 map
一样,Swift 标准库中的数组类型已经有定义好的filter
函数了。所以除非是作为练
//习,否则并没有必要重写它
extension Array {
func filter (includeElement:
Element -> Bool) -> [Element] {
var result: [Element] = []
for x in
self where includeElement(x) {
result . append(x)
}
return result
}
}
func getSwiftFiles2( files: [String]) -> [String] {
return files.filter {
file in file .hasSuffix(".swift")
}
}
getSwiftFiles2(exampleFiles)
// Reduce
/**
下面函数有什么共同:
它们都将变量 result 初始化为某个值。随后对输入数组 xs
的每一项 进行遍历,最后以某种方式更新结果。
*/
func sum(xs: [Int]) ->
Int {
var result: Int =
0
for x in xs {
result += x
}
return result
}
func product(xs:[Int]) ->
Int {
var result: Int =
1
for x in xs {
result = x * result
}
return result
}
/**
想要连接数组中的所有字符串
*/
func concatenate(xs: [String]) ->
String {
var result: String =
""
for x in xs {
result += x
}
return result
}
func prettyPrintArray(xs: [String]) ->
String {
var result:
String =
"Entries in the array xs:\n"
for x in xs {
result = " " + result + x +
"\n"
}
return result
}
/**
* 为了定义一个可以体现所需类型的泛型函数,需要
对两份信息进行抽象:赋给 result
变量的初始值,和用于在每一次循环中更新 result
的函数
这个函数的泛型体现在两个方面:对于任意 [Element]
类型的输入数组来说,它会计算一个类
型为 T
的返回值。这么做的前提是,首先需要一个 T
类型的初始值 (赋给 result
变量),以及一
个用于更新 for
循环中变量值的函数 combine: (T, Element) -> T。在一些像 OCaml
和 Haskell
一样的函数式语言中,reduce
函数被称为 fold 或 fold_left
*/
extension Array {
func reduce<T>(initial:
T, combine: (T,
Element) -> T) ->
T {
var result = initial
for x in
self {
result = combine(result, x)
}
return result
}
}
// 用 reduce
来定义上面的函数
func sumUsingReduce(xs: [Int]) ->
Int {
return xs.reduce(0) { result, x
in result + x }
}
// 除了写一个闭包,也可以将运算符作为最后一个参数,这使得代码更短
//自定义 reduce
仅仅只是为了练习,Swift
的标准库已经为数组提供了 reduce
函数
func productUsingReduce(xs: [Int]) ->
Int {
return xs.reduce(1, combine: *)
}
func concatUsingReduce(xs: [String]) ->
String {
return xs.reduce("", combine: +)
}
/**
假设有一个数组,它的每一项都是数组,而
我们想将它展开为一个单一数组。
可以使用 for
循环编写一个函数
*/
func atten<T>(xss: [[T]]) -> [T] {
var result: [T] = []
for xs in xss {
result += xs
}
return result
}
// 然而,若使用 reduce
则可以像下面这样编写这个函数
func attenUsingReduce<T>(xss: [[T]]) -> [T] {
return xss.reduce([]) { result, xs
in result + xs }
}
//实际上,我们甚至可以使用 reduce
重新定义 map
和 filter
extension Array {
func mapUsingReduce<T>(transform:
Element -> T) -> [T] {
return reduce([]) {
result, x in
return result + [transform(x)]
}
}
func filterUsingReduce(includeElement:
Element -> Bool) -> [Element] {
return reduce([]) {
result, x in
return includeElement(x) ? result + [x]:result
}
}
}
// 实际运用
struct City {
let name: String
let population:
Int
}
let paris = City(name:
"Paris", population:
2241)
let madrid = City(name:
"Madrid", population:
3165)
let amsterdam = City(name:
"Amsterdam", population:
827)
let berlin = City(name:
"Berlin", population:
3562)
let cities = [paris,
madrid, amsterdam,
berlin]
// 想筛选出居⺠数量至少一百万的城市,并打印一份这些城市的名字及总人口数的
//列表。我们可以定义一个辅助函数来换算居⺠数量
extension City {
func cityByScalingPopulation() ->
City {
return City(name:
name, population:
population * 1000)
}
}
/**
* 首先将居⺠数量少于一百万的城市过滤掉。
然后将剩下的结果通过 cityByScalingPopulation
函数进行 map
操作。
最后,使用 reduce
函数来构建一个包含城市 名字和人口数量列表的 String。
这里使用了 Swift
标准库中 Array 类型的 map、filter
和 reduce
定义。
于是,可以顺利地链式使用过滤和映射的结果。表达式 cities . filter (..)
的结果是一个数组,
对其调用 map;然后这个返回值调用 reduce
即可得到最终结果
*/
cities .filter { $0.population >
1000 }
.map { $0.cityByScalingPopulation() }
.reduce("City: Population") {
result, c in
return result +
"\n" + "\(c.name):
\(c.population)"
}
/**
*
泛型和 Any
类型
Swift 还支持 Any
类型,它能代表任何类型的值。从表面上看,这好像和泛型极其
相似。Any
类型和泛型两者都能用于定义接受两个不同类型参数的函数。然而,理解两者之间
的区别至关重要:泛型可以用于定义灵活的函数,类型检查仍然由编译器负责;而
Any 类型则 可以避开 Swift
的类型系统 (所以应该尽可能避免使用)
*/
//构想一个函数,除了返回它的参数,其它什么也不做。如果使
//用泛型,我们可能写为下面这样:
/**
noOp 和 noOpAny
两者都将接受任意参数。关键的区别在于我们所知道的返回值。在 noOp
的 定义中,我们可以清楚地看到返回值和输入值完全一样。而 noOpAny
的例子则不太一样,返回
值是任意类型 — 甚至可以是和原来的输入值不同的类型。
*/
func noOp<T>(x: T) ->
T {
return x
}
//而使用 Any
类型,则可能写为这样:
func noOpAny(x: Any) ->
Any {
return x
}
//给出一个 noOpAny
的错误
定义
//使用 Any
类型可以避开 Swift 的类型系统。然而,尝试将使用泛型定义的 noOp
函数返回值设 为 0
将会导致类型错误
func noOpAnyWrong(x:
Any) -> Any {
return
0
}
/**
* 我们需要得到的是一个 A -> C
类型的函数。由于我们并不知道其它任何有关 C
的信息,所以暂
时没有能够返回的值。如果知道 C
是像 Int 或者 Bool
这样的具体类型的话,我们就可以返回一
个该类型的值,例如分别返回 5
或 True。然而函数必须能处理任意类型的 C,所以我们不能轻率地返回具体值。在 >>>
运算符的参数中,只有 g: B -> C
函数提及了类型 C。因此,将 B
类型 的值传递给函数 g
是我们能够触及类型 C 的唯一途径。
同样,要得到一个 B
类型值的唯一方法是将类型为 A
的值传递给 f。类型为 A
的值唯一出现的 地方是在我们运算符要求返回的函数的输入参数里。因此,函数组合的定义只有这唯一一种可
能,才能满足所要求的泛型类型。
*/
infix operator >>> {
associativity left }
func >>> <A, B, C>(f:
A -> B, g:
B -> C) ->
A -> C {
return { x in g(f(x))
}
}
//可以用相同的方式定义一个泛型函数,该函数能够将任意的接受两个元素的元组作为输入
的函数进行柯里化 (curry)
处理,从而生成相应的柯里化版本
func curry<A,B,C>(f: (A,B)->C)->A->B->C{
return { x in { y
in f(x, y)}
}
}
// 泛型
接受其它函数作为参数的函数有时被称为高阶函数。
Swift 标准库
中作用于数组的高阶函数中漫游。Swift
泛型,展示如复杂计算运用于数组
*/
/*
顶层函数和扩展
写一个函数,它接受一个给定的整型数组,通过计算得到并返回一个新数组,新
数组各项为原数组中对应的整型数据加一。这一切,仅仅只需要使用一个 for
循环就能非常容 易地实现
*/
func incrementArray(xs:[Int]) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(x +
1) }
return result
}
/**
*/
func doubleArray1(xs:[Int]) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(x *
2) }
return result
}
var tmpArray = doubleArray1([1,2,3,4])
/// 两个函数有大量相同的代码,我们能不能将没有区别的地方抽象出来,并单独写一个体现这
// 种模式且更通用的函数
func computeIntArray(xs: [Int], transform:
Int -> Int ) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(transform(x))
}
return result
}
/**
* 现在,取决于我们想如何根据原数组得到一个新数组,我们可以向函数传递不同的参数。
doubleArray 函数和 incrementArray
函数都精简为了一行调用 computeIntArray
的语句:
*/
func doubleArray2(xs:[Int]) -> [Int] {
return
computeIntArray(xs) {
x in x * 2
}
}
func computeBoolArray(xs: [Int], transform:
Int -> Bool) -> [Bool] {
var result: [Bool] = []
for x in xs {
result . append(transform(x))
}
return result
}
func isEvenArray(xs: [Int ]) -> [Bool] {
return computeBoolArray(xs) { x
in x % 2 ==
0}
}
/**
* 这个方案的扩展性并不好。如果接下来需要计算 String
类型呢?
是否还需要定义另
一个高阶函数来接受 Int -> String
类型的参数
解决方案:使用泛型
genericComputeArray<T>
理解为一个函数族,类型参数 T
的每个选择都会确定一个新函数
该函数接受一个整型数组和一个 Int -> T类型的函数作为参数,并返回一个[T]类型的数组
*/
func genericComputeArray<T>(xs: [Int], transform:
Int -> T) -> [T] {
var result: [T] = []
for x in xs {
result.append(transform(x))
}
return result
}
/**
进一步将这个函数一般化。没有理由让它仅能对类型为 [Int]
的输入数组进行处理
将数组类型进行抽象,能得到下面这样的类型签名
写了一个 map
函数,它在两个维度都是通用的:对于任何 Element
的数组和 transform: Element -> T
函数,它都会生成一个 T
的新数组。
这个 map
函数甚至比我们之前 看到的 genericComputeArray
函数更通用。
*/
func map<Element,T>(xs:[Element],transform:Element ->T)->[T]{
var result :[T] = []
for x in xs{
result.append(transform(x))
}
return result
}
/**
* 事实上,可以通过 map
来定义 genericComputeArray:
*/
func genericComputeArray2<T>(xs: [Int], transform:
Int -> T) -> [T] {
return map(xs, transform: transform)
}
// 按照 Swift
的惯例将 map 定义为 Array
的扩展会更合
适
extension Array {
func map<T>(transform:
Element -> T) -> [T] {
var result: [T] = []
for x in
self {
result . append(transform(x)) }
return result
}
}
/**
map(xs, transform) 的替代,我们现在可以通过 xs.map(transform)
来调用 Array的map
函数
定义 map
函数,它已经是 Swift
标准库的
一部分了(实际上,它基于 SequenceType
协议被定义)。
map 的定义中并
没有什么复杂难懂的魔法 ,能够轻松地自己定义它
*/
func genericComputeArray_<T>(xs: [Int], transform:
Int -> T) -> [T] {
return xs.map(transform)
}
var mapArray =
genericComputeArray_(tmpArray) {
x in x
}
// Filter
let exampleFiles = ["README.md",
"HelloWorld.swift", "FlappyBird.swift"]
func getSwiftFiles( files: [String]) -> [String] {
var result: [String] = []
for file in files {
if file .hasSuffix(".md") {
result . append(file)
}
}
return result
}
getSwiftFiles(exampleFiles)
// MARK: - 想查找没有扩展名的所有文
件,或者是名字以字符串 "Hello"
开头的文件
// 为了进行一个这样的查找,我们可以定义一个名为 filter
的通用型函数。就像之前看到的 map
那样, lter 函数接受一个函数作为参数。 filter
函数的类型是 Element -> Bool ——
对于数 组中的所有元素,此函数都会判定它是否应该被包含在结果中
// 就像 map
一样,Swift 标准库中的数组类型已经有定义好的filter
函数了。所以除非是作为练
//习,否则并没有必要重写它
extension Array {
func filter (includeElement:
Element -> Bool) -> [Element] {
var result: [Element] = []
for x in
self where includeElement(x) {
result . append(x)
}
return result
}
}
func getSwiftFiles2( files: [String]) -> [String] {
return files.filter {
file in file .hasSuffix(".swift")
}
}
getSwiftFiles2(exampleFiles)
// Reduce
/**
下面函数有什么共同:
它们都将变量 result 初始化为某个值。随后对输入数组 xs
的每一项 进行遍历,最后以某种方式更新结果。
*/
func sum(xs: [Int]) ->
Int {
var result: Int =
0
for x in xs {
result += x
}
return result
}
func product(xs:[Int]) ->
Int {
var result: Int =
1
for x in xs {
result = x * result
}
return result
}
/**
想要连接数组中的所有字符串
*/
func concatenate(xs: [String]) ->
String {
var result: String =
""
for x in xs {
result += x
}
return result
}
func prettyPrintArray(xs: [String]) ->
String {
var result:
String =
"Entries in the array xs:\n"
for x in xs {
result = " " + result + x +
"\n"
}
return result
}
/**
* 为了定义一个可以体现所需类型的泛型函数,需要
对两份信息进行抽象:赋给 result
变量的初始值,和用于在每一次循环中更新 result
的函数
这个函数的泛型体现在两个方面:对于任意 [Element]
类型的输入数组来说,它会计算一个类
型为 T
的返回值。这么做的前提是,首先需要一个 T
类型的初始值 (赋给 result
变量),以及一
个用于更新 for
循环中变量值的函数 combine: (T, Element) -> T。在一些像 OCaml
和 Haskell
一样的函数式语言中,reduce
函数被称为 fold 或 fold_left
*/
extension Array {
func reduce<T>(initial:
T, combine: (T,
Element) -> T) ->
T {
var result = initial
for x in
self {
result = combine(result, x)
}
return result
}
}
// 用 reduce
来定义上面的函数
func sumUsingReduce(xs: [Int]) ->
Int {
return xs.reduce(0) { result, x
in result + x }
}
// 除了写一个闭包,也可以将运算符作为最后一个参数,这使得代码更短
//自定义 reduce
仅仅只是为了练习,Swift
的标准库已经为数组提供了 reduce
函数
func productUsingReduce(xs: [Int]) ->
Int {
return xs.reduce(1, combine: *)
}
func concatUsingReduce(xs: [String]) ->
String {
return xs.reduce("", combine: +)
}
/**
假设有一个数组,它的每一项都是数组,而
我们想将它展开为一个单一数组。
可以使用 for
循环编写一个函数
*/
func atten<T>(xss: [[T]]) -> [T] {
var result: [T] = []
for xs in xss {
result += xs
}
return result
}
// 然而,若使用 reduce
则可以像下面这样编写这个函数
func attenUsingReduce<T>(xss: [[T]]) -> [T] {
return xss.reduce([]) { result, xs
in result + xs }
}
//实际上,我们甚至可以使用 reduce
重新定义 map
和 filter
extension Array {
func mapUsingReduce<T>(transform:
Element -> T) -> [T] {
return reduce([]) {
result, x in
return result + [transform(x)]
}
}
func filterUsingReduce(includeElement:
Element -> Bool) -> [Element] {
return reduce([]) {
result, x in
return includeElement(x) ? result + [x]:result
}
}
}
// 实际运用
struct City {
let name: String
let population:
Int
}
let paris = City(name:
"Paris", population:
2241)
let madrid = City(name:
"Madrid", population:
3165)
let amsterdam = City(name:
"Amsterdam", population:
827)
let berlin = City(name:
"Berlin", population:
3562)
let cities = [paris,
madrid, amsterdam,
berlin]
// 想筛选出居⺠数量至少一百万的城市,并打印一份这些城市的名字及总人口数的
//列表。我们可以定义一个辅助函数来换算居⺠数量
extension City {
func cityByScalingPopulation() ->
City {
return City(name:
name, population:
population * 1000)
}
}
/**
* 首先将居⺠数量少于一百万的城市过滤掉。
然后将剩下的结果通过 cityByScalingPopulation
函数进行 map
操作。
最后,使用 reduce
函数来构建一个包含城市 名字和人口数量列表的 String。
这里使用了 Swift
标准库中 Array 类型的 map、filter
和 reduce
定义。
于是,可以顺利地链式使用过滤和映射的结果。表达式 cities . filter (..)
的结果是一个数组,
对其调用 map;然后这个返回值调用 reduce
即可得到最终结果
*/
cities .filter { $0.population >
1000 }
.map { $0.cityByScalingPopulation() }
.reduce("City: Population") {
result, c in
return result +
"\n" + "\(c.name):
\(c.population)"
}
/**
*
泛型和 Any
类型
Swift 还支持 Any
类型,它能代表任何类型的值。从表面上看,这好像和泛型极其
相似。Any
类型和泛型两者都能用于定义接受两个不同类型参数的函数。然而,理解两者之间
的区别至关重要:泛型可以用于定义灵活的函数,类型检查仍然由编译器负责;而
Any 类型则 可以避开 Swift
的类型系统 (所以应该尽可能避免使用)
*/
//构想一个函数,除了返回它的参数,其它什么也不做。如果使
//用泛型,我们可能写为下面这样:
/**
noOp 和 noOpAny
两者都将接受任意参数。关键的区别在于我们所知道的返回值。在 noOp
的 定义中,我们可以清楚地看到返回值和输入值完全一样。而 noOpAny
的例子则不太一样,返回
值是任意类型 — 甚至可以是和原来的输入值不同的类型。
*/
func noOp<T>(x: T) ->
T {
return x
}
//而使用 Any
类型,则可能写为这样:
func noOpAny(x: Any) ->
Any {
return x
}
//给出一个 noOpAny
的错误
定义
//使用 Any
类型可以避开 Swift 的类型系统。然而,尝试将使用泛型定义的 noOp
函数返回值设 为 0
将会导致类型错误
func noOpAnyWrong(x:
Any) -> Any {
return
0
}
/**
* 我们需要得到的是一个 A -> C
类型的函数。由于我们并不知道其它任何有关 C
的信息,所以暂
时没有能够返回的值。如果知道 C
是像 Int 或者 Bool
这样的具体类型的话,我们就可以返回一
个该类型的值,例如分别返回 5
或 True。然而函数必须能处理任意类型的 C,所以我们不能轻率地返回具体值。在 >>>
运算符的参数中,只有 g: B -> C
函数提及了类型 C。因此,将 B
类型 的值传递给函数 g
是我们能够触及类型 C 的唯一途径。
同样,要得到一个 B
类型值的唯一方法是将类型为 A
的值传递给 f。类型为 A
的值唯一出现的 地方是在我们运算符要求返回的函数的输入参数里。因此,函数组合的定义只有这唯一一种可
能,才能满足所要求的泛型类型。
*/
infix operator >>> {
associativity left }
func >>> <A, B, C>(f:
A -> B, g:
B -> C) ->
A -> C {
return { x in g(f(x))
}
}
//可以用相同的方式定义一个泛型函数,该函数能够将任意的接受两个元素的元组作为输入
的函数进行柯里化 (curry)
处理,从而生成相应的柯里化版本
func curry<A,B,C>(f: (A,B)->C)->A->B->C{
return { x in { y
in f(x, y)}
}
}
相关文章推荐
- SWift开发:使用委托/Protocol 实现类之间的数据传递
- Swift中方法闭包参数不能省略括号的一种情况
- Swift中方法闭包参数不能省略括号的一种情况
- Swift中方法闭包参数不能省略括号的一种情况
- 【Swift学习】Swift编程之旅---析构方法(十九)
- 对照Java学习Swift--继承(Inheritance)
- 传智Swift入门视频第20期
- 《Swift入门》如何在Windows或者ubuntu下安装XCode6环境来开发Swift?
- swift 跳转网页写法
- Swift -- 5.集合
- swift 柯里化
- Swift学习第一枪
- swift开发使用闭包传值
- iOS swift判断摄像头权限
- [swift学习之五]类练习
- Swift 基本语法详解(2)
- Swift 基本语法详解(1)
- 【Swift学习】Swift编程之旅---构造方法(十八)
- Swift 基本语法
- [swift学习之四]结构体练习