如何在集合类型约束中使用泛型?

3

我一直在尝试从字符串数组中提取非零值。就像下面这样。但是,我的前辈希望它能够从其他类型中提取非空值。

我了解到,泛型可以帮助我处理不同的类型。我如何使用泛型,以便我可以使用以下扩展来处理其他类型呢?

getNonNil必须返回特定类型的提取的非零值(即,如果数组是[String?],它必须返回[String],如果是[Int?],则返回[Int])

因为我需要进行进一步的计算。

我尝试的方法如下:

import Foundation
// Extended the collection-type so that collectiontype is constrained to having element with optional strings
extension CollectionType where Self.Generator.Element == Optional<String>{
    func getNonNil() -> [String] {
        // filter out all nil elements and forcefully unwrap them using map
        return self.filter({$0 != nil}).map({$0!})
    }
}

// Usage
let x: [String?] = ["Er", "Err", nil, "errr"]

x.getNonNil().forEach { (str) in
    print(str)
}
3个回答

4

对于 getNonNil,您可以简单地使用

x.flatMap { $0 }
// returns ["Er", "Err", "errr"] which is [String]

针对原始问题,通常可以通过引入协议到Optional类型来解决(例如通过muukii/OptionalProtocol包):

protocol OptionalProtocol {
    associatedtype Wrapped
    var value: Wrapped? { get }
}

extension Optional: OptionalProtocol {
    public var value: Wrapped? { return self }
}

extension CollectionType where Self.Generator.Element: OptionalProtocol {
    func getNonNil() -> [Self.Generator.Element.Wrapped] {
        ...
    }
}

flatMap 已经可用。但在第二个解决方案中,我该如何使用它?你能给我展示一个例子吗?因为当我尝试时,我得到了“致命错误:在解包可选值时意外发现 nil”或其他错误。 - Bibek
@Dari 你会想在 getNonNil 方法中执行 return flatMap {$0.value} - Hamish

2

通过扩展没有简单的方法来实现这一点,因为您无法在扩展中引入新的泛型类型(尽管这是Swift通用程序清单的一部分,所以在未来版本的Swift中可能会有可能)。

正如@kennytm所说的那样,最简单的解决方案就是使用flatMap,它可以过滤掉nil

x.flatMap{$0}.forEach { (str) in
    print(str)
}

然而,如果您仍希望将其作为扩展程序,您可以使用协议解决方法,以便允许您将扩展程序限制为任何可选元素类型(Swift 3):
protocol _OptionalProtocol {
    associatedtype Wrapped
    func _asOptional() -> Wrapped?
}

extension Optional : _OptionalProtocol {
    func _asOptional() -> Wrapped? {return self}
}

extension Collection where Self.Iterator.Element : _OptionalProtocol {
    func getNonNil() -> [Iterator.Element.Wrapped] {
        return flatMap{$0._asOptional()}
    }
} 

...

let x : [String?] = ["Er", "Err", nil, "errr"]

x.getNonNil().forEach { (str) in
    print(str)
}

(在 Swift 3 中,CollectionType 已更名为 Collection,Generator 现在是 Iterator)
尽管在这种情况下几乎肯定更喜欢使用 flatMap,但我只是出于完整性的考虑添加了这个。

0

最简单的方法是使用 flatMap,就像kennytm建议的那样。但是如果你绝对想知道如何使用泛型创建这样的方法,一种方法是创建一个全局方法,将集合作为参数传入:

public func getNonNil<T, C: CollectionType where C.Generator.Element == Optional<T>>(collection: C) -> [T] {
    return collection.filter({$0 != nil}).map({$0!})
}

let x: [String?] = ["Er", "Err", nil, "errr"]

print(getNonNil(x)) // returns ["Er", "Err", "errr"]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接