Important to note that this isn’t a stable sort algorithm, as demonstrated by the following code:
struct Ace {
let i:Int
let s:String
}
func ==(lhs: Ace, rhs: Ace) -> Bool {
return lhs.i == rhs.i
}
func <(lhs: Ace, rhs: Ace) -> Bool {
return lhs.i < rhs.i
}
extension Ace: Comparable {}
let array = [Ace(i: 1,s: "a"),Ace(i: 1,s: "b"),Ace(i: 1,s: "c"),Ace(i: 1,s: "d"),Ace(i: 1,s: "e"),Ace(i: 1,s: "f")]
print(mergeSort(array)) // [Ace(i: 1, s: "a"), Ace(i: 1, s: "d"), Ace(i: 1, s: "b"), Ace(i: 1, s: "e"), Ace(i: 1, s: "c"), Ace(i: 1, s: "f")]
Whereas the C function mergesort is stable:
struct Ace {
let i:Int
let s:String
}
func ==(lhs: Ace, rhs: Ace) -> Bool {
return lhs.i == rhs.i
}
func <(lhs: Ace, rhs: Ace) -> Bool {
return lhs.i < rhs.i
}
extension Ace: Comparable {}
let array = [Ace(i: 1,s: "a"),Ace(i: 1,s: "b"),Ace(i: 1,s: "c"),Ace(i: 1,s: "d"),Ace(i: 1,s: "e"),Ace(i: 1,s: "f")]
mergesort(&array, array.count, MemoryLayout<Ace>.size, {
let a = $0.unsafelyUnwrapped.load(as:Ace.self)
let b = $1.unsafelyUnwrapped.load(as:Ace.self)
if a == b {
return 0
}
else if a < b {
return -1 }
else {
return 1
}
})
array // [{i 1, s "a"}, {i 1, s "b"}, {i 1, s "c"}, {i 1, s "d"}, {i 1, s "e"}, {i 1, s "f"}]
Or, Airspeed Velocity wrote a pure Swift stable sort:
// stable sort taken from AirspeedVelocity blog
// http://airspeedvelocity.net/2016/01/10/writing-a-generic-stable-sort/
extension RangeReplaceableCollection
where
Index: Strideable,
SubSequence.Iterator.Element == Iterator.Element,
IndexDistance == Index.Stride {
public mutating func stableSortInPlace(
isOrderedBefore: @escaping (Iterator.Element,Iterator.Element)->Bool
) {
let N = self.count
var aux: [Iterator.Element] = []
aux.reserveCapacity(numericCast(N))
func merge(lo: Index, mid: Index, hi: Index) {
aux.removeAll(keepingCapacity: true)
var i = lo, j = mid
while i < mid && j < hi {
if isOrderedBefore(self[j],self[i]) {
aux.append(self[j])
j = (j as! Int + 1) as! Self.Index
}
else {
aux.append(self[i])
i = (i as! Int + 1) as! Self.Index
}
}
aux.append(contentsOf:self[i..<mid])
aux.append(contentsOf:self[j..<hi])
self.replaceSubrange(lo..<hi, with: aux)
}
var sz: IndexDistance = 1
while sz < N {
for lo in stride(from:startIndex, to: endIndex-sz, by: sz*2) {
// see https://swift.org/migration-guide/
if let hiVal = self.index(lo, offsetBy:sz*2, limitedBy: endIndex) {
merge(lo:lo, mid: lo+sz, hi:hiVal)
}
}
sz *= 2
}
}
}