iosgenericsswift5

I was disappointed to discover Comparable seems to have no .min call? Is that correct or am I missing something?


I just did this,

1, 5, 100, 2, 7 becomes 5, 100, 7

1, 2, 18, 19, 5, 4 becomes 2, 19, 5

extension Array where Element: Comparable {
    
    ///Simply return the bigger of each pair of (say) integers as we go
    ///along. (If there's an odd count, of course the last solo one is
    ///just that value.)
    var biggerOfPairs: [Element] {
//        var safely: [Element] = []
//        if self.count % 2 == 1 { safely.append(Element.min) }
//        var result: [Element] = []
//        for i in 0..<(safely.count/2) {
//            result.append(Swift.max(safely[i * 2], safely[(i * 2) + 1]))
//        }
        // damnit! ask on SO
        var result: [Element] = []
        for i in 0..<(self.count/2) {
            result.append(Swift.max(self[i * 2], self[(i * 2) + 1]))
        }
        if self.count % 2 == 1 {
            result.append(self[self.count - 1])
        }
        return result
    }
}

(I use the usual trick if you need a list to be in, say, groups of three, just add the appropriate "dud" element(s) on the end, if the size of the list doesn't divide by three; so that you can run an exception-free algorithm. (The "dud" elements might be empty views, zero, nil, or whatever is relevant.) Hence if this extension was just on [Int] you'd make the "dud" one at the end Int.min.)

but sadly,

sad days

It seems hard to believe you would write a Comparable protocol without a min/max representable concept.

(They have a note in the doco about exceptional concepts like NaN, but still. Since when could you program along the lines "oh don't worry about corner cases." - !)

Could it be I'm missing something?

(*) Maybe there's a tighter protocol to use that includes a lowest representable value?

FTR at one point an error appeared suggesting (any Comparable).min, but that didn't seem to pan out (and I don't understand it anyway).


Solution

  • It doesn't make sense for all Comparable things to have a max or min.

    There are comparable things that can arbitrarily large, like String, or BigInt. The "maximum value" of these types would be very time-consuming to create, and take a lot of memory.

    There are comparable things that have multiple "maximum values". For example, a class like

    class Foo: Comparable {
        var value: Int // this class implements Comparable based on this
    
        // initialiser and operator implementations omitted
    }
    
    let x = Foo(value: .max)
    let y = Foo(value: .max)
    

    x and y are both "maximum values" for Foo, but these are distinct class instances. In fact, your implementation of biggerOfPairs could return a Foo instance that is not in the original array if Foo.min simply returns a new instance (return Foo(value: .min)). This is because max returns the second argument if the two arguments are equal.

    Technically this also applies to floating point types, since IEEE 754 allows multiple representations of "infinity", but it is not that much of a problem in practice.

    I would recommend creating your own protocol like this:

    protocol HasMinMax {
        var min: Self { get }
        var max: Self { get }
    }
    

    FixedWidthInteger offers the min and max properties. FloatingPoint offers infinity, and you can use -.infinity for min. You can retroactively conform types that conform to FixedWidthInteger and FloatingPoint to HasMinMax, but you cannot directly do

    extension FixedWidthInteger: HasMinMax { ... }
    extension FloatingPoint: HasMinMax { ... }