Last active
June 20, 2022 03:58
-
-
Save jenox/788495f449ff3a22f272e627b20933af to your computer and use it in GitHub Desktop.
CartesianProduct<_,_>
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// must be in different module | |
public func blackhole<T>(_ value: T) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
for (x, y) in [1, 2, 3].cartesianProduct(with: ["a", "b"]) { | |
print(x, y) // [(1, "a"), (1, "b"), (2, "a"), (2, "b"), (3, "a"), (3, "b")] | |
} | |
func measure(closure: () -> Void) -> String { | |
let before = CFAbsoluteTimeGetCurrent() | |
closure() | |
let after = CFAbsoluteTimeGetCurrent() | |
return String(format: "%.3fms", 1e3 * (after - before)) | |
} | |
func measureIteration<T>(over sequence: T) -> String where T: Sequence { | |
return measure(closure: { | |
for x in sequence { | |
blackhole(x) | |
} | |
}) | |
} | |
func loopBasedCartesianProduct<C1: Collection, C2: Collection>(_ lhs: C1, _ rhs: C2) { | |
for x in lhs { | |
for y in rhs { | |
blackhole((x,y)) | |
} | |
} | |
} | |
let integers = Array(0..<10000) | |
print("baseline:", measure(closure: { loopBasedCartesianProduct(integers, integers) })) | |
print("cartesian:", measureIteration(over: integers.cartesianProduct(with: integers))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Swift | |
public struct CartesianProduct<Base1, Base2> where Base1: Collection, Base2: Collection { | |
@usableFromInline internal let base1: Base1 | |
@usableFromInline internal let base2: Base2 | |
@inlinable | |
internal init(base1: Base1, base2: Base2) { | |
self.base1 = base1 | |
self.base2 = base2 | |
} | |
} | |
extension CartesianProduct { | |
public struct Iterator { | |
@usableFromInline internal var base1Iterator: Base1.Iterator | |
@usableFromInline internal var base1Element: Base1.Element? | |
@usableFromInline internal let base2: Base2 | |
@usableFromInline internal var base2Iterator: Base2.Iterator | |
@inlinable | |
internal init(base1: Base1, base2: Base2) { | |
self.base1Iterator = base1.makeIterator() | |
self.base1Element = self.base1Iterator.next() | |
self.base2 = base2 | |
self.base2Iterator = base2.makeIterator() | |
} | |
} | |
} | |
extension CartesianProduct.Iterator: IteratorProtocol { | |
public typealias Element = (Base1.Element, Base2.Element) | |
@inlinable | |
public mutating func next() -> Element? { | |
guard let base1Element = self.base1Element else { return nil } | |
if let element = self.base2Iterator.next() { | |
return (base1Element, element) | |
} else { | |
self.base1Element = self.base1Iterator.next() | |
guard let base1Element = self.base1Element else { return nil } | |
self.base2Iterator = self.base2.makeIterator() | |
if let element = self.base2Iterator.next() { | |
return (base1Element, element) | |
} else { | |
self.base1Element = nil | |
return nil | |
} | |
} | |
} | |
} | |
extension CartesianProduct: Sequence { | |
public typealias Element = (Base1.Element, Base2.Element) | |
@inlinable | |
public func makeIterator() -> Iterator { | |
return Iterator(base1: self.base1, base2: self.base2) | |
} | |
@inlinable | |
public var underestimatedCount: Int { | |
return self.base1.underestimatedCount * self.base2.underestimatedCount | |
} | |
} | |
// TODOs: Collection, BidirectionalCollection, RandomAccessCollection conformances | |
//extension CartesianProduct: Collection {} | |
//extension CartesianProduct: BidirectionalCollection where Base1: BidirectionalCollection, Base2: BidirectionalCollection {} | |
//extension CartesianProduct: RandomAccessCollection where Base1: RandomAccessCollection, Base2: RandomAccessCollection {} | |
extension Collection { | |
@inlinable | |
public func cartesianProduct<C>(with other: C) -> CartesianProduct<Self, C> where C: Collection { | |
return CartesianProduct(base1: self, base2: other) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment