【问题标题】:How to generically handle enumerate(collection)'s different return types for Arrays and Dictionaries?如何一般处理数组和字典的枚举(集合)的不同返回类型?
【发布时间】:2014-09-04 21:52:19
【问题描述】:

作为 Swift 新手,作为练习练习,我正在尝试编写一个等效于 underscore.js'_.each 的 Swift:

_.each({foo: true, bar: false}, function(value, key) {...}
_.each([true, false], function(value, key) {...}

但事实证明这很困难,因为 enumerate(collection) 会为字典和数组返回不同类型的结果:

for (indexInt, (key, value)) enumerate(["foo": true]) {...}
for (key, value) enumerate([true]) {...}

如何处理这两种不同的返回类型而不明确检查泛型集合的ArrayDictionary 类?

返回类型检查将失败,因为以下两个示例应区别对待,但从 enumerate() 返回等效类型:

for (indexInt, (key, value)) enumerate(["foo": true]) {...}
for (indexInt, (key, value)) enumerate([("foo", true")]) {...}

另外,FWIW,$.swift 似乎在这方面下注:

https://github.com/ankurp/Dollar.swift/blob/master/Dollar/Dollar/Dollar.swift#L193-L217

编辑:格式化。还有。。

一个损坏的通用实现

func each<C: Collection>(collection: C, fn: (Any, Any) -> ()) -> C {
    // On next line... Error: Tuple pattern cannot match values of the non-tuple type 'C.GeneratorType.Element'
    for (_, (k, v)) in enumerate(collection) {
        if let innerV = collection[innerK] {
            fn(innerV, k)
        }
    }
    return collection
}
var ret = each(["foo":true, "bar":false]) { println("Key \($1), Value:\($0)") }

一个有效的字典实现

func eachDictionary<K,V>(collection: Dictionary<K, V>, fn: (V, K) -> ()) -> Dictionary<K, V> {
    for (_, (k, v)) in enumerate(collection) {
        fn(v, k)
    }
    return collection
}
var retDictionary = eachDictionary(["foo":true, "bar":false]) { println("Key \($1), Value:\($0)") }

一个有效的数组实现

func each<V>(collection: V[], fn: (V, Int) -> ()) -> V[] {
    for (k, v) in enumerate(collection) {
        fn(v, k)
    }
    return collection
}
var retArray = each([true, false]) { println("Key \($1), Value:\($0)") }

编辑 2:更新了使用重载的示例。 我特别想问是否有一种通用的 Collection Protocol 方式来实现这一点,而不需要对每个 Collection 类型进行一次性处理?

编辑 3:我尝试了以下操作(我必须在空数组的情况下添加更多类型,这样它就不会抱怨回调的参数不可打印)但是...

  1. 现在字典失败了(传递 (K,V) 作为值)
  2. [(V1,V2)] 案例失败
  3. 我非常想找到一种方法来使用泛型(K, V) 而不是(Any, Any)

代码:

func each<C: Collection>(collection: C, fn: (Any, Any) -> ()) -> C {
    for (k, v) in enumerate(collection) {
        if let (key, value) = v as? (Any, Any) {
            fn(value, key)
        } else {
            fn(v, k)
        }
    }
    return collection
}
var ret = each(["foo":true, "bar":false]) { println("1: Key \($1), Value:\($0)") }
var arr : String[] = []
var retOne = each(arr) { println("2: Key \($1), Value:\($0)") }
var retTwo = each([true, false]) { println("3: Key \($1), Value:\($0)") }

【问题讨论】:

    标签: arrays generics dictionary swift each


    【解决方案1】:

    JavaScript 和 Swift 之间最大的区别之一是您可以定义具有相同名称但具有不同签名的(函数|方法)(重载)。好好利用吧。

    class $ {
        class func map<K:Hashable,V,R>
            (dictionary:[K:V], block:(V,K)->R)->[K:R] {
            var result = [K:R]()
            for (k, v) in dictionary {
                result[k] = block(v, k)
            }
            return result
        }
        class func map<V,R>
            (array:[V], block:(V,Int)->R)->[R] {
            var result = [R]()
            for (k, v) in enumerate(array) {
                result.append(block(v, k))
            }
            return result
        }
        class func each<K:Hashable,V,R>
            (dictionary:[K:V], block:(V,K)->R) {
            for (k, v) in dictionary { block(v, k) }
        }
        class func each<V,R>
            (array:[V], block:(V,Int)->R) {
            for (k, v) in enumerate(array) { block(v, k) }
        }
    }
    
    println($.map(["zero":0, "one":1], { n,_ in n+1 }))
    println($.map([0,1],               { n,_ in n+1 }))
    $.each(["zero":0, "one":1]) { println("\($1) => \($0)") }
    $.each([0, 1])              { println("\($1) => \($0)") }
    

    【讨论】:

    • 我正在寻找一个通用的收集协议解决方案。我可以使用“each”而不是“eachArray”和“eachDictionary”来完成相同的任务,但我必须为每个 Collection 类型实现它。 Dictionary vs Array 问题只是一个失败点,虽然我不确定是否有好的解决方案。
    【解决方案2】:

    枚举不是你的问题。字典是。您尝试做的事情不太可能奏效,因为这不是基于 Collection 的含义。字典中的键与传递给数组的整数无关。 Dictionary 的索引是DictionaryIndex,而不是KeyType

    首先,让我们考虑如何将它写成一个泛型函数(它不会做你想要的,但正是你可以在 Collection 上做的):

    func each<C:Collection>(collection: C, fn:(C.GeneratorType.Element, C.IndexType) -> ()) -> C {
      for index in indices(collection) {
        fn(collection[index], index)
      }
      return collection
    }
    

    这会遍历集合并将其值和索引传递给某个接受它们的函数。对于数组,此函数将具有签名:

    (T, Int) -> ()
    

    对于字典,此函数将具有签名

    (ValueType, DictionaryIndex<KeyType, ValueType>) -> ()
    

    您对enumerate 的使用只是掩盖了这个潜在问题。集合是一个序列,它在通过下标传递索引时返回一个值。就是这样。序列是可以通过生成器生成值的东西。就是这样。在两者之间,他们对字典键一无所知。从通用的角度来看,字典只是键/值元组的序列。

    现在,您希望 做到的是字典的索引是它的键。但事实并非如此。没有通用的方法来讨论 Dictionary 的键,因为它们不是 Collection 协议的一部分(并且 Array 和 Dictionary 没有其他可以挂起的通用协议)。

    那么,你可能会问,为什么你可以说dict[key] 并得到一个值?这与收藏无关。这是 Dictionary 中的一个额外的下标方法(我的 cmets):

    // This is the Collection subscript
    subscript (i: DictionaryIndex<KeyType, ValueType>) -> (KeyType, ValueType) { get }
    
    // This is a subscript unrelated to Collection
    subscript (key: KeyType) -> ValueType?
    

    由于 Collection 对字典的键一无所知,因此几乎可以肯定不可能找到解决此问题的通用解决方案。

    我怀疑 Dictionary 的键不是索引的原因与 dict[key] 返回一个 Optional 的事实有关,而 array[num](通常是 Collection 的下标)返回实际值。

    【讨论】:

    • 谢谢。这正是我得出的结论。在签名中使用关联类型的奖励积分。字典在 Collection 上下文中似乎是相对独特的,因为它们的下标索引与其 Collection 索引不同。有没有一种通用的方法来提取下标索引类型?如果可以,这真的没关系,因为它与值类型的关系尚不清楚。非常感谢!抱歉,我没有足够的业力来投票。
    猜你喜欢
    • 2013-01-05
    • 1970-01-01
    • 2019-12-29
    • 2019-06-08
    • 2012-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-26
    相关资源
    最近更新 更多