0

I'm trying to extend a Collection of FloatingPoint-conforming elements to work out an average.

extension Collection where Element: FloatingPoint {
    func sum() -> Element {
        return reduce(0, +)
    }

    func average() -> Element {
        return sum() / Int(count)
    }
}

sum() works fine but average() has an error.

Binary operator '/' cannot be applied to operands of type 'Self.Element' and 'Int'

I'm not sure why this is. Self.Element is a FloatingPoint. I would expect to be able to divide this.

(I'm also aware that there's a divide-by-zero issue, but I'll fix that later.)

Josh Paradroid
  • 1,172
  • 18
  • 45

1 Answers1

3

You average didnt work because you were trying to divide some FloatingPoint type with integer. Use Element(count) to create new element of same type to divide.

  extension Collection where Element: FloatingPoint {
      func sum() -> Element {
        return reduce(0, +)
      }

      func average() -> Element {
        guard !isEmpty else { return 0 }
        return sum() / Element(count)
      }
    }

And this works because FloatingPoint protocol declares following initializer,

public init(_ value: Int)

This works with Swift 4.1, since count is Int. For earlier versions of Swift use,

  extension Collection where Element: FloatingPoint {
      func sum() -> Element {
        return reduce(0, +)
      }

      func average() -> Element {
        guard !isEmpty else { return 0 }
        return sum() / Element(Int(count))
      }

  }
Sandeep
  • 20,908
  • 7
  • 66
  • 106
  • 1
    I assume that you tested this with Swift 4.1/Xcode 9.3 beta. In earlier versions you'll need `Element(Int(count))` because the `count` is not necessarily an `Int`. – Martin R Feb 20 '18 at 16:20
  • Your code gives me the following error: `Cannot invoke 'Element' with an argument list of type '(Self.IndexDistance)'` Do you know what's with that? – dvp.petrov Feb 20 '18 at 16:24
  • 2
    @dvp.petrov you can add a constraint to the Collection `where IndexDistance == Int, Index == Int` – Leo Dabus Feb 20 '18 at 16:25
  • You can simply use `Element(count)` if you extend Array – Leo Dabus Feb 20 '18 at 16:33
  • @LeoDabus I'm not a fan of extending `Array`, because it leaves `ArraySlice` in the dark, and it's really useful to have the same operations available to both – Alexander Feb 20 '18 at 16:44
  • @Alexander Anyway This won't work when extending collection (Swift 4) if OP doesn't constrain also the IndexDistance and the Index type to Int – Leo Dabus Feb 20 '18 at 16:45
  • @LeoDabus Indeed, no disagreement – Alexander Feb 20 '18 at 16:45
  • 1
    This works with Swift 4 since, count is Int in Swift 4 is wrong. You mean "This works with Swift 4.1 since, count is Int in Swift 4.1 for Swift 4 or earlier use following," – Leo Dabus Feb 20 '18 at 16:49
  • Thanks @LeoDabus Done, If you see anything wrong with this, just write it here. :) – Sandeep Feb 20 '18 at 16:54
  • 1
    @Sandeep Not wrong but you can check the link I posted above. You can extend Numeric protocol for the sum to extend all numeric types and for the zero division add an isEmpty ternary condition – Leo Dabus Feb 20 '18 at 16:55