美文网首页
Swift | Building a Custom Collec

Swift | Building a Custom Collec

作者: 清無 | 来源:发表于2022-02-17 18:22 被阅读0次

    In this tutorial, you’re going to build a multiset, otherwise known as a bag, from scratch.

    Along the way, you’ll learn how to:

    • Adopt these protocols: Hashable, Sequence, Collection, CustomStringConvertible, ExpressibleByArrayLiteral and ExpressibleByDictionaryLiteral.
    • Create custom initializations for your collections.
    • Improve your custom collections with custom methods.

    Getting Started

    A Bag is like a Set in that it does not store repeated values. The difference is this: A Bag keeps a running count of any repeated values while a Set does not.

    Set vs Bag

    Initializing Collections

    To avoid this, Swift supplies two protocols that enable initialization with sequence literals. Literals give you a shorthand way to write data without explicitly creating an object.

    // 1
    extension Bag: ExpressibleByArrayLiteral {
      init(arrayLiteral elements: Element...) {
        self.init(elements)
      }
    }
    
    // 2
    extension Bag: ExpressibleByDictionaryLiteral {
      init(dictionaryLiteral elements: (Element, Int)...) {
        self.init(elements.map { (key: $0.0, value: $0.1) })
      }
    }
    
    • ExpressibleByArrayLiteral is used to create a Bag from an array style literal. Here you use the initializer you created earlier and pass in the elements collection.
    • ExpressibleByDictionaryLiteral does the same but for dictionary style literals. The map converts elements to the named-tuple the initializer expects.

    Enforcing Non-destructive Iteration

    One caveat: Sequence does not require conforming types to be non-destructive. This means that after iteration, there’s no guarantee that future iterations will start from the beginning. That’s a huge issue if you plan on iterating over your data more than once.

    To enforce non-destructive iteration, your object needs to conform to the Collection protocol.

    Collection inherits from Indexable and Sequence.

    Collection

    Final Codes

    import Foundation
    
    struct Bag<Thing: Hashable> {
        fileprivate var contents: [Thing: Int] = [:]
        
        var thingsKinds: Int {
            return contents.count
        }
        
        var thingsCount: Int {
            return contents.values.reduce(0){ $0 + $1 }
        }
        
        init(){}
        
        init<S: Sequence>(_ list: S) where S.Iterator.Element == Thing {
            list.forEach{ put($0) }
        }
        
        init<S: Sequence>(_ tuple: S) where S.Iterator.Element == (key: Thing, value: Int) {
            tuple.forEach{ put($0.key, num: $0.value) }
        }
        
        mutating func put(_ thing: Thing, num: Int = 1) {
            precondition(num > 0, "Should put at least 1 thing in bag!")
            
            if let currentNum = contents[thing] {
                contents[thing] = currentNum + num
            }
            else {
                contents[thing] = num
            }
        }
        
        mutating func take(_ thing: Thing, num: Int = 1) {
            precondition(num > 0, "Should take at least 1 thing from bag!")
            
            guard
                let currentCount = contents[thing],
                currentCount >= num else {
                    return
                }
            if currentCount > num {
                contents[thing] = currentCount - num
            }
            else {
                contents.removeValue(forKey: thing)
            }
        }
    }
    
    extension Bag: CustomStringConvertible {
        var description: String {
            return String(describing: contents)
        }
    }
    
    extension Bag: ExpressibleByDictionaryLiteral {
        init(dictionaryLiteral elements: (Thing, Int)...) {
            self.init(elements.map{ (key: $0.0, value: $0.1) })
        }
    }
    
    extension Bag: ExpressibleByArrayLiteral {
        init(arrayLiteral elements: Thing...) {
            self.init(elements)
        }
    }
    
    
    struct BagIndex<Element: Hashable> {
        fileprivate let index: DictionaryIndex<Element, Int>
        
        fileprivate init(
            _ dictionaryIndex: DictionaryIndex<Element, Int>) {
                self.index = dictionaryIndex
            }
    }
    
    extension BagIndex: Comparable {
        static func == (lhs: BagIndex, rhs: BagIndex) -> Bool {
            return lhs.index == rhs.index
        }
        
        static func < (lhs: BagIndex, rhs: BagIndex) -> Bool {
            return lhs.index < rhs.index
        }
    }
    
    extension Bag: Sequence {
        typealias Iterator = AnyIterator<(thing: Thing, num: Int)>
        
        func makeIterator() -> Iterator {
            var iterator = contents.makeIterator()
            
            return AnyIterator {
                return iterator.next()
            }
        }
    }
    
    extension Bag: Collection {
        typealias Index = BagIndex<Thing>
        
        var startIndex: BagIndex<Thing> {
            return .init(contents.startIndex)
        }
        
        var endIndex: BagIndex<Thing> {
            return .init(contents.endIndex)
        }
        
        subscript (position: Index) -> Iterator.Element {
            precondition((startIndex ..< endIndex).contains(position),
                         "out of bounds")
            // 3
            let dictionaryElement = contents[position.index]
            return (thing: dictionaryElement.key,
                    num: dictionaryElement.value)
        }
        
        func index(after i: Index) -> Index {
            // 4
            return Index(contents.index(after: i.index))
        }
    }
    
    
    var bag = Bag<String>()
    bag.put("Book")
    bag.put("Food", num: 2)
    
    let bag1: Bag = ["A", "B"]
    bag1
    
    let bag2: Bag = ["A": 1, "B": 2]
    bag2
    
    bag.forEach{
        print($0)
    }
    

    相关文章

      网友评论

          本文标题:Swift | Building a Custom Collec

          本文链接:https://www.haomeiwen.com/subject/zfwrlrtx.html