加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

swift 泛型

发布时间:2020-12-14 02:35:14 所属栏目:百科 来源:网络整理
导读:/** // 泛型 接受其它函数作为参数的函数有时被称为高阶函数。 Swift 标准库 中作用于数组的高阶函数中漫游。Swift 泛型,展示如复杂计算运用于数组 */ /* 顶层函数和扩展 写一个函数,它接受一个给定的整型数组,通过计算得到并返回一个新数组,新 数组各项为原

/**
 // 泛型
 接受其它函数作为参数的函数有时被称为高阶函数。
 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,C>(f: (A,B)->C)->A->B->C{
    return { x in { y in f(x,y)}
    }
}



(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读