I'm trying to write a generic median function. As finding a median involves sorting the collection, but I don't want to copy the collection upfront, I sort the indices instead, and then fetch the median.
But this does not work: accessing the collection from the closure consumes it. Adding s
to the capture list is still consumption.
func median<T>(_ s: borrowing T,
averagingWith avgFcn: (T.Element, T.Element) -> T.Element)
// Comparable doesn't necessarily have an averaging function
-> T.Element?
where T : RandomAccessCollection, T.Element : Comparable {
switch s.count {
// cases where the sort isn't necessary
case 1:
return s.first!
case 2:
return avgFcn(s.first!, s.last!)
// Have multiple, will sort
case 3...:
let idx = s.indices.sorted(by: { s[$0] < s[$1] })
// error: 's' is borrowed and cannot be consumed
// `- note: consumed here
let (quotient: mid, remainder: rem) = idx.count.quotientAndRemainder(dividingBy:2)
let midlarger = s[idx[idx.indices[mid]]]
if (rem != 0) { // odd number of elements
return midlarger
} else { // even number of elements...
let midsmaller = s[idx[idx.indices[mid-1]]]
if (midlarger == midsmaller) { // but don't call average just yet
return midlarger
} else {
return avgFcn(midsmaller, midlarger)
}
}
default:
return nil // 0 elements
}
}
Any idea how to make it work?
EDIT: oops forgot Swift's index is starts from 0