首页 > 解决方案 > 带有可选元素的扩展可选数组。甚至可能吗?

问题描述

我有一个协议FooProtocol。和一堂课Bar<Foo:FooProtocol>var mess: [Foo?]?在一个类中保留一个数组,[foo1, foo2, nil, foo3...]或者nil 我尝试对此数组进行扩展以计算新的 Foo 对象。我更喜欢有协议,因为 Foos 可能是从外部世界传递的非常不同的对象。

protocol FooProtocol {
  ....
  init(from heaven: Int)
}

extension Optional where
  Wrapped: Collection,
  Wrapped.Element == Optional,
  Wrapped.Element.Wrapped: FooProtocol // 'Wrapped' is not a member type of 'Wrapped.Element'
{
  var united: Wrapped.Element.Wrapped { // Nope
    let i = ...
    return Wrapped.Element.Wrapped(from: i) // Nope
    
  }
}

class Bar<Foo:FooProtocol> {
  var mess: [Foo?]?
  init (with mess: [Foo?]?) {
    self.mess = mess
  }
  var important: Foo {
    return mess.united
  }
}

有任何想法吗?我被封锁了。

编辑1:

在 Leo 提出建议后,我更改了部分代码。但还是卡住了。这次来自 Playgrounds 的更多代码。

任何可以转换为“[Double]”的对象。可以是颜色(如 RGBA)、Bezier 曲线、正方形等等……

public protocol FooProtocol {
    var atomized: () -> [Double] {get}
    static var count: Int {get}
    init(_ array:[Double])
    init()
}


public extension Array where Element: FooProtocol {
    var average: Element {
        var resultAtoms: [Double] = []
        let inputAtoms = self.map {$0.atomized()}
        for i in 0..<Element.count {
            let s = inputAtoms.reduce(into: 0.0, {$0 += $1[i]}) / Double (Element.count)
            resultAtoms.append(s)
        }
        return Element(resultAtoms)
    }
}

extension Optional where
    Wrapped: Collection,
    Wrapped.Element == Optional<FooProtocol>
{
    typealias Foo = Wrapped.Element.Wrapped // Doesn't work. How to get class?
    
    var average: Foo { // I cannot use Wrapped.Element, it's Optional
        if let thatsList = self {
            let withOptionals = Array(thatsList) // OK, its [Optional<FooProtocol>]
            let withoutOptionals = thatsList.compactMap({$0}) // OK, its [FooProtocol]
            // This is funny, called from class works and makes 'bingo'.
            return withoutOptionals.average // Error: Value of protocol type 'FooProtocol' cannot conform to 'FooProtocol'; only struct/enum/class types can conform to protocols
        } else {
            return Foo() // Hello? init Wrapped? Foo? How to get Foo()?
        }
    }
}

class Bar<Foo:FooProtocol> {
    var mess: [Foo?]?
    init (with mess: [Foo?]?) {
        self.mess = mess
    }
    func workOn() {
        let z:Foo = mess.average  // OK, I can make 'mess.average ?? Foo()' but prefer not do it
    }
    // Thats OK
    func workHard() { // To prove 'Array extension where Element: FooProtocol' works
        if let messExist = mess {
            let withoutOptionals =  messExist.compactMap({$0})
            let bingo = withoutOptionals.average //It's OK
        }
    }
}

class SomeFoo : FooProtocol {
    static var count = 3
    required init() {
        a = 0
        b = 0
        c = 0
    }
    
    required init(_ array: [Double]) {
        self.a = Int(array[0])
        self.b = Float(array[1])
        self.c = array[2]
    }
    
    var atomized: () -> [Double]  {
        return {return [Double(self.a), Double(self.b), self.c]}
    }
    
    var a: Int
    var b: Float
    var c: Double
}

let aFoo = SomeFoo([1, 2, 3])
let bFoo = SomeFoo([7, 9, 1])
let cFoo = SomeFoo([2, 6, 5])

let barData = [nil, aFoo, nil, bFoo, cFoo]
let barWithData = Bar(with: barData)
let barWithoutData = Bar<SomeFoo>(with: nil)

也许我应该忘记扩展数组并在类中创建一些函数(我几乎可以肯定我会在其他地方需要这些函数)

编辑 2

即使我尝试简化并为 Array 进行扩展,我也发现了麻烦。

extension Array where
    Element == Optional<FooProtocol>
{
    func averageNils <Foo: FooProtocol>() -> Foo {
        
        let withOptionals = Array(self) // OK, its [Optional<FooProtocol>]
        let withoutOptionals = self.compactMap({$0}) // OK, its [FooProtocol]
        return withoutOptionals.average as! Foo // Error: Value of protocol type 'FooProtocol' cannot conform to 'FooProtocol'; only struct/enum/class types can conform to protocols
    }
}

标签: swiftgenerics

解决方案


根据我的理解,它应该像你一样工作,但是人们永远不知道在 swift 编译器世界中会发生什么(尤其是错误消息)。

无论如何,您可以通过将更精确地Wrapped.Element.Wrapped指定为a来避免深入挖掘 :Wrapped.ElementOptional<FooProtocol>

protocol FooProtocol {}
class Foo : FooProtocol {}

extension Optional where
  Wrapped: Collection, //OK
  Wrapped.Element == Optional<FooProtocol> // still good
{
   var unfied: Wrapped.Element  // Should be 'Foo' if self is '[Foo?]?' {
   {
    return 1 == 0 ? nil : Foo()
   }
}

推荐阅读