【问题标题】:Generic type operands in custom Swift operator自定义 Swift 运算符中的泛型类型操作数
【发布时间】:2020-01-09 18:35:55
【问题描述】:

我的目标是创建一个运算符,让我可以轻松合并Dictionary 值,例如:

["a" : "A"] + ["b" : "B"]

我在下面的尝试:

extension Dictionary {
    public static func +=<K, V>(lhs: inout Dictionary<K, V>, rhs: Dictionary<K, V>?) {
        if let rhs = rhs {
            lhs.merge( rhs )
        }
    }

    public static func +=<K, V>(lhs: inout Dictionary<K, V>?, rhs: Dictionary<K, V>) {
        if var lhs = lhs {
            lhs.merge( rhs )
        }
        else {
            lhs = rhs
        }
    }

    public static func +<K, V>(lhs: Dictionary<K, V>, rhs: Dictionary<K, V>?) -> Dictionary<K, V> {
        if let rhs = rhs {
            return lhs.merging( rhs )
        }

        return lhs
    }

    public static func +<K, V>(lhs: Dictionary<K, V>?, rhs: Dictionary<K, V>) -> Dictionary<K, V> {
        if let lhs = lhs {
            return lhs.merging( rhs )
        }

        return rhs
    }

    @inlinable public mutating func merge(_ other: [Key: Value]) {
        self.merge( other, uniquingKeysWith: { $1 } )
    }

    @inlinable public func merging(_ other: [Key: Value]) -> [Key: Value] {
        self.merging( other, uniquingKeysWith: { $1 } )
    }
}

这似乎不起作用(从 Swift 5 开始),我怀疑这个问题与 Dictionary 类型是泛型类型有关。任何人都可以解释是什么阻碍了它的工作以及是否有其他可行的解决方案/方法?

【问题讨论】:

  • 老实说,我不会推荐这个。 dictA.merging(dictB, uniquingKeysWith: { $1 } ) 并不长,而且很清楚发生了什么。而只是将其掩饰为merging,目前尚不清楚正在使用哪种冲突解决方法。让我们一起使用++=,如果不遵循所有实现面包屑,没人会知道发生了什么。

标签: swift operators


【解决方案1】:

问题在于您正在创建新的通用 KV 值类型。您只需要在方法声明中省略它们即可。顺便说一句,您忘记添加一个方法来总结两个非可选字典:

extension Dictionary {

    public static func +=(lhs: inout Dictionary, rhs: Dictionary) {
         lhs.merge(rhs)
    }
    public static func +=(lhs: inout Dictionary, rhs: Dictionary?) {
        if let rhs = rhs { lhs.merge(rhs) }
    }
    public static func +=(lhs: inout Dictionary?, rhs: Dictionary) {
        if var lhs = lhs { lhs.merge(rhs) } else { lhs = rhs }
    }

    public static func +(lhs: Dictionary, rhs: Dictionary) -> Dictionary {
        lhs.merging(rhs)
    }
    public static func +(lhs: Dictionary, rhs: Dictionary?) -> Dictionary {
        if let rhs = rhs { return lhs.merging(rhs) }
        return lhs
    }
    public static func +(lhs: Dictionary?, rhs: Dictionary) -> Dictionary {
        if let lhs = lhs { return lhs.merging(rhs) }
        return rhs
    }

    @inlinable public mutating func merge(_ other: Dictionary) {
        self.merge(other) {$1}
    }

    @inlinable public func merging(_ other: Dictionary) -> Dictionary {
        self.merging(other) {$1}
    }
}

let dicSum = ["a" : "A"] + ["b" : "B"]

dicSum  // ["b": "B", "a": "A"]

另一种方法是为 Dictionary 泛型 Key 和 Value 类型创建类型别名,并删除您在方法声明 &lt;K,V&gt; 中创建的那些,否则它们将再次成为与 Dictionary declaration 中定义的类型无关的新泛型类型:

extension Dictionary {

    public typealias K = Key
    public typealias V = Value

    public static func +=(lhs: inout [K: V], rhs: [K: V]) {
         lhs.merge(rhs)
    }
    public static func +=(lhs: inout [K: V], rhs: [K: V]?) {
        if let rhs = rhs { lhs.merge(rhs) }
    }
    public static func +=(lhs: inout [K: V]?, rhs: [K: V]) {
        if var lhs = lhs { lhs.merge(rhs) } else { lhs = rhs }
    }

    public static func +(lhs: [K: V], rhs: [K: V]) -> [K: V] {
        lhs.merging(rhs)
    }
    public static func +(lhs: [K: V], rhs: [K: V]?) -> [K: V] {
        if let rhs = rhs { return lhs.merging(rhs) }
        return lhs
    }
    public static func +(lhs: [K: V]?, rhs: [K: V]) -> [K: V] {
        if let lhs = lhs { return lhs.merging(rhs) }
        return rhs
    }

    @inlinable public mutating func merge(_ other: [K: V]) {
        self.merge(other) {$1}
    }

    @inlinable public func merging(_ other: [K: V]) -> [K: V] {
        self.merging(other) {$1}
    }
}

这与上面所做的完全相同。


同样DictionaryDictionary&lt;Key, Value&gt;[Key:Value] 完全一样:

extension Dictionary {

    public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) {
         lhs.merge(rhs)
    }
    public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]?) {
        if let rhs = rhs { lhs.merge(rhs) }
    }
    public static func +=(lhs: inout [Key: Value]?, rhs: [Key: Value]) {
        if var lhs = lhs { lhs.merge(rhs) } else { lhs = rhs }
    }

    public static func +(lhs: [Key: Value], rhs: [Key: Value]) -> [Key: Value] {
        lhs.merging(rhs)
    }
    public static func +(lhs: [Key: Value], rhs: [Key: Value]?) -> [Key: Value] {
        if let rhs = rhs { return lhs.merging(rhs) }
        return lhs
    }
    public static func +(lhs: [Key: Value]?, rhs: [Key: Value]) -> [Key: Value] {
        if let lhs = lhs { return lhs.merging(rhs) }
        return rhs
    }

    @inlinable public mutating func merge(_ other: [Key: Value]) {
        self.merge(other) {$1}
    }

    @inlinable public func merging(_ other: [Key: Value]) -> [Key: Value] {
        self.merging(other) {$1}
    }
}

实现它的所有 3 种方法都是等效的。选择你觉得舒服的那个。

【讨论】:

  • 泛型类型的想法是强制参数和返回值的类型需要兼容。省略那些泛型类型,编译器如何知道字典的类型是兼容的(mergemerging 之一,要求类型兼容),甚至+ 的返回值应该键入为?我想我不知道为什么这甚至会起作用?
  • Dictionary KeyValue 如您所知是泛型类型。唯一的要求是密钥必须是Hashable。您可以使用 merge 方法组合任何字典类型,只要求另一个 Sequence Element 需要是键值对。 developer.apple.com/documentation/swift/dictionary/…。请注意,如果将两个具有不同键类型的字典组合在一起,则生成的字典 Key 将被推断为 AnyHashable,如果您组合不同的 Value 类型,则生成的 Value 类型将被推断为 Any
  • 那么Swift是如何解析+的返回值的KeyValue呢?它是基于操作数的吗?方法签名似乎不包含任何将操作数的Key 与返回值的Key 相关的内容。同样,+= 运算符的merge 是否要求lhsKeyValue 类型与rhs 的类型相同,这里如何满足这个要求?还是这个解决方案只是完全消除了所有泛型类型和约束?
  • merge+= 不允许您将 Dictionary 与不同的键值类型合并
  • 我不确定这是否回答了KeyValue 类型在操作数之间以何种方式相互关联并返回Dictionarys 的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
相关资源
最近更新 更多