Last active
August 29, 2015 14:07
-
-
Save Revolucent/1a8085746b950d91a0a8 to your computer and use it in GitHub Desktop.
Minimum/maximum implementations for SequenceType and Arrays in Swift
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
/* | |
Replace the name of my Swift module, Prosumma, with your own, or this will not compile. | |
As of 1.0, Swift lacks mixins (as in Ruby) or the ability to extend protocols (as in C#), which means lots of | |
cumbersome boilerplate. I've tried to cut down on that with this little snippet. First, you'll notice that | |
there are no actual minimum and maximum functions defined. To achieve that, do the following: | |
var lowest: Int? = [5, 4, 3].most(<) | |
var highest: Int? = [5, 4, 3].most(>) | |
Because of certain limitations of Swift's type system, type inference will not work here, so you have to say Int? | |
explicitly or it will not work. | |
Since it is not possible to create an extension on SequenceType directly, I created two global functions, both called | |
most. The first one allows the implementer to make a member of the sequence comparable by using a closure. Let me illustrate | |
what that means. Let's take MapKit's MKRoute class, which has a distance property. We want to compare MKRoutes by | |
their distance in order to find the shortest route. We could of course make MKRoute implement Comparable, but instead | |
we decide to use the second parameter of most as follows: | |
var shortestRoute: MKRoute? = most(routes, { $0.distance }, <) | |
This compares routes by their distance and returns the route (NOT the distance) with the shortest distance. If routes is empty, | |
most returns nil. | |
The second version of most is simpler. It can only be used with sequences whose elements are comparable, e.g. | |
var numbers = [2, 7, 4, 3] | |
var lowest: Int? = most(numbers, <) | |
The rest is pretty straightforward: a protocol called Most which contains object-oriented versions of the most functions, | |
with the first parameter implicit. Then I provided an implementation for Array using an extension. The methods in Array | |
forward to the global functions, allowing the following: | |
var shortestRoute: MKRoute? = routes.most({ $0.distance }, <) | |
var lowest: Int? = numbers.most(<) | |
Thanks to the global functions, Most can be added to any SequenceType fairly quickly. | |
*/ | |
public func most<S: SequenceType, C: Comparable>(sequence: S, comparable: S.Generator.Element -> C, compare: (C, C) -> Bool) -> S.Generator.Element? { | |
var est: (S.Generator.Element, C)? = nil | |
for elem in sequence { | |
if est == nil { | |
est = (elem, comparable(elem)) | |
} else { | |
var comparison = comparable(elem) | |
if compare(comparison, est!.1) { | |
est = (elem, comparison) | |
} | |
} | |
} | |
return est == nil ? nil : est!.0 | |
} | |
public func most<C: Comparable, S: SequenceType where S.Generator.Element == C>(sequence: S, compare: (C, C) -> Bool) -> C? { | |
var est: C? = nil | |
for elem in sequence { | |
if est == nil { | |
est = elem | |
} else if compare(elem, est!) { | |
est = elem | |
} | |
} | |
return est | |
} | |
public protocol Most : SequenceType { | |
func most<C: Comparable>(comparable: Self.Generator.Element -> C, _ compare: (C, C) -> Bool) -> Self.Generator.Element? | |
func most<C: Comparable>(compare: (C, C) -> Bool) -> C? | |
} | |
extension Array : Most { | |
public func most<C: Comparable>(comparable: T -> C, _ compare: (C, C) -> Bool) -> T? { | |
return Prosumma.most(self, comparable, compare) | |
} | |
public func most<C: Comparable>(compare: (C, C) -> Bool) -> C? { | |
return Prosumma.most(map({ $0 as C }), compare) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment