让我的函数计算Swift数组的平均值

38

我希望我的函数能够计算出Double类型数组的平均值。该数组名为"votes",目前共有10个数字。

当我调用average函数来获取数组"votes"的平均值时,它无法正常工作。

以下是我的代码:

var votes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

func average(nums: Double...) -> Double {
    var total = 0.0
    for vote in votes {
        total += vote
    }
    let votesTotal = Double(votes.count)
    var average = total/votesTotal
    return average
}

average[votes]

我该如何调用这里的平均值来获取平均数?


平均值(votes)。将其声明为average(nums: [Double]),并确保votes数组为[Double]。现在它是一个[Int]。 - MirekE
7个回答

134

您应使用reduce方法来按以下方式求和您的序列元素:

Xcode Xcode 10.2+ • Swift 5或更高级版本

extension Sequence where Element: AdditiveArithmetic {
    /// Returns the total sum of all elements in the sequence
    func sum() -> Element { reduce(.zero, +) }
}

extension Collection where Element: BinaryInteger {
    /// Returns the average of all elements in the array
    func average() -> Element { isEmpty ? .zero : sum() / Element(count) }
    /// Returns the average of all elements in the array as Floating Point type
    func average<T: FloatingPoint>() -> T { isEmpty ? .zero : T(sum()) / T(count) }
}

extension Collection where Element: BinaryFloatingPoint {
    /// Returns the average of all elements in the array
    func average() -> Element { isEmpty ? .zero : sum() / Element(count) }
}

let votes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let votesTotal = votes.sum()                       // 55
let votesAverage = votes.average()                 // 5
let votesDoubleAverage: Double = votes.average()   // 5.5

如果你需要使用 Decimal 类型并计算它们的总和,可以直接使用 AdditiveArithmetic 协议扩展方法实现,因此只需实现平均值即可:

extension Collection where Element == Decimal {
    func average() -> Decimal { isEmpty ? .zero : sum() / Decimal(count) }
}

如果您需要对自定义结构的某个属性进行求和,可以扩展Sequence并创建一个接受KeyPath作为参数来计算其总和的方法:
extension Sequence  {
    func sum<T: AdditiveArithmetic>(_ predicate: (Element) -> T) -> T {
        reduce(.zero) { $0 + predicate($1) }
    }
}

使用方法:
struct User {
    let name: String
    let age: Int
}

let users: [User] = [
    .init(name: "Steve", age: 45),
    .init(name: "Tim", age: 50)]

let ageSum = users.sum(\.age) // 95

将集合扩展以计算其平均值:

extension Collection {
    func average<T: BinaryInteger>(_ predicate: (Element) -> T) -> T {
        sum(predicate) / T(count)
    }
    func average<T: BinaryInteger, F: BinaryFloatingPoint>(_ predicate: (Element) -> T) -> F {
        F(sum(predicate)) / F(count)
    }
    func average<T: BinaryFloatingPoint>(_ predicate: (Element) -> T) -> T {
        sum(predicate) / T(count)
    }
    func average(_ predicate: (Element) -> Decimal) -> Decimal {
        sum(predicate) / Decimal(count)
    }
}

用法:

let ageAvg = users.average(\.age)                 // 47
let ageAvgDouble: Double = users.average(\.age)   // 47.5

1
在Swift 2中,我得到了“[Double]”类型的值没有“IntegerLiteralType”的成员。 - Burf2000
这个能用于 Decimal 数组吗?代码 extension Array where Element: Decimals 抛出了一个错误。 - highboi
属性应该在O(1)的时间内工作,或者适当的注释应该说明情况。在totalaverage的情况下,我会使用方法而不是计算属性。请参见此处的常规约定中的第一条规则:https://swift.org/documentation/api-design-guidelines/ - Yoav
1
@Yoav 你说的有道理,“reduce” 的复杂度确实是O(n),但我认为大多数开发人员会期望这样常见的操作通过属性来暴露。方法和属性之间存在不匹配将是更严重的问题。请记住,它们只是惯例,而不是规则。 - Womble

8

你的代码中有一些错误:

//You have to set the array-type to Double. Because otherwise Swift thinks that you need an Int-array
var votes:[Double] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

func average(nums: [Double]) -> Double {

    var total = 0.0
    //use the parameter-array instead of the global variable votes
    for vote in nums{
        total += Double(vote)
    }

    let votesTotal = Double(nums.count)
    var average = total/votesTotal
    return average
}

var theAverage = average(votes)

4

2

我有一组信号是在更新函数内创建的,为了获得移动平均值,我使用这个函数,在移动平均期间内计算平均值。由于我的目标是组装一个包含平均值的新信号集合,因此我将放弃原始信号集中的信号。对于那些想要在更新函数内(例如SKScene)获得移动平均值的人来说,这是一个很好的解决方案。

func movingAvarage(_ period: Int) -> Double? {
  if signalSet.count >= period {
   let window = signalSet.suffix(period)
   let mean = (window.reduce(0, +)) / Double(period)
   signalSet = signalSet.dropLast(period)
   return mean
  }
  return nil
}

2

如果需要,可以使用过滤器进行简单平均(Swift 4.2):

let items: [Double] = [0,10,15]
func average(nums: [Double]) -> Double {
    let sum = nums.reduce((total: 0, elements: 0)) { (sum, item) -> (total: Double, elements: Double) in
        var result = sum
        if item > 0 { // example for filter
            result.total += item
            result.elements += 1
        }

        return result
    }

    return sum.elements > 0 ? sum.total / sum.elements : 0
}
let theAvarage = average(nums: items)

1

Swift 4.2

对于纯粹的优雅简洁,我喜欢:

// 1. Calls #3
func average <T> (_ values: T...) -> T where T: FloatingPoint
{
    return sum(values) / T(values.count)
}

顺便提一下,其他不错的基于reduce的操作:

// 2. Unnecessary, but I appreciate variadic params. Also calls #3.
func sum <T> (_ values: T...) -> T where T: FloatingPoint
{
    return sum(values)
}

// 3.
func sum <T> (_ values: [T]) -> T where T: FloatingPoint
{
    return values.reduce(0, +)
}

信用:Adrian Houdart的MathKit, 没有太多改动。


可爱的更新:

我在Swift编程语言中找到了以下内容:

The example below calculates the arithmetic mean (also known as the average) for a list of numbers of any length:

func arithmeticMean(_ numbers: Double...) -> Double {
   var total: Double = 0
   for number in numbers {
       total += number
   }
   return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers

0
将这些扩展添加到Array中将计算Int和FloatingPoint值的平均值。
extension Array where Element: BinaryInteger {

  var average: Element? {
      guard let elementCount = Element(exactly: count) else { return nil }
      return reduce(0) { $0 + $1 } / elementCount
  }
}

extension Array where Element: FloatingPoint {

var average: Element? {
      guard let elementCount = Element(exactly: count) else { return nil }
      return reduce(0) { $0 + $1 } / elementCount
  }
}

已接受的答案已经提供了这样的扩展,但比仅适用于Array更加通用。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接