Swift数组扩展,以n个先前值的总和替换索引n的值

时间:2018-10-02 17:40:03

标签: arrays swift extension-methods reduce

我正在尝试为数组类型编写一个扩展,以对索引n中的n个先前的索引求和。

let myArray = [1, 2, 3, 4, 5]
let mySumArray = myArray.sumNIndex()
print(mySumArray)
// returns [1,3,6,10,15]

我尝试了各种方法,但都失败了。 例如,以下示例触发编译错误 “ 无法使用类型为((Int,_)'的参数列表来调用'reduce'”:

extension Array {
    mutating func indexSum() {
        var tempArray = [Any]()
        for index in 1...self.count - 1 {
        self[index] += self[.prefix(index + 2).reduce(0, +)]
        }
    }
}

这另一次尝试触发另一个编译错误: “ 二进制运算符'+ ='不能应用于两个'Element'操作数

extension Array {
    mutating func indexSum() {
        var tempArray = [Any]()
        for index in 1...self.count - 1 {
        self[index] += self[index - 1]
        }
    }
}

任何想法都欢迎! 非常感谢您的帮助!

编辑:非常感谢@Martin和@Carpsen用两种不同的方式弄清楚了这一点

@Martin使用地图方法:

extension Array where Element: Numeric {
    func cumulativeSum() -> [Element] {
        var currentSum: Element = 0
        return map {
            currentSum += $0
            return currentSum
        }
    }
}

@Carpsen使用归约方法:

extension Array where Element: Numeric {
    func indexSum() -> [Element] {
        return self.reduce(into: [Element]()) {(acc, element) in
            return acc + [(acc.last ?? 0) + element]
        })
    }
}

2 个答案:

答案 0 :(得分:1)

主要问题是未为元素定义加法运算符+ 任意数组。您需要限制扩展方法,例如至 Numeric个元素的数组。

也无需使用Any

以下是一种作为非变异方法的实现方式:

extension Array where Element: Numeric {
    func cumulativeSum() -> [Element] {
        var currentSum: Element = 0
        return map {
            currentSum += $0
            return currentSum
        }
    }
}

示例:

let intArray = [1, 2, 3, 4, 5]
print(intArray.cumulativeSum()) // [1, 3, 6, 10, 15]

let floatArray = [1.0, 2.5, 3.25]
print(floatArray.cumulativeSum()) [1.0, 3.5, 6.75]

以类似的方式,我们可以“累积地加入” 字符串数组。 enumerated()现在用于提供当前元素 与元素一起索引,用于决定是否 是否插入分隔符:

extension Array where Element == String {
    func cumulativeJoin(separator: String) -> [Element] {
        var currentJoin = ""
        return enumerated().map { (offset, elem) in
            if offset > 0 { currentJoin.append(separator) }
            currentJoin.append(elem)
            return currentJoin
        }
    }
}

示例:

let stringArray = ["a", "b", "c"]
print(stringArray.cumulativeJoin()) // ["a", "ab", "abc"]
print(stringArray.cumulativeJoin(separator: ":")) // ["a", "a:b", "a:b:c"]

答案 1 :(得分:0)

尝试一下:

let myArray = [1, 2, 3, 4, 5]

myArray.reduce([Int](), {accumulator, element in
    return accumulator + [(accumulator.last ?? 0) + element]
})
//[1, 3, 6, 10, 15]

reduce的作用是:

  • 以空数组开头
  • 对于myArray中的每个元素,它使用accumulator
  • 中的最后一个元素来计算其 sum
  • 返回上一个数组加上最后一个求和

这是一个更简单但更长的版本:

let myArray = [1, 2, 3, 4, 5]

let newArray = myArray.reduce([Int](), {accumulator, element in
    var tempo = accumulator
    let lastElementFromTheAccumulator = accumulator.last ?? 0
    let currentSum = element + lastElementFromTheAccumulator
    tempo.append(currentSum)
    return tempo
})

print(newArray)  //[1, 3, 6, 10, 15]

Martin R 在评论中建议的更有效的解决方案使用reduce(into:)

myArray.reduce(into: [Int]()) { (accumulator, element) in
    accumulator += [(accumulator.last ?? 0) + element]
}
//[1, 3, 6, 10, 15]

您可以将其作为扩展名:

extension Array where Element: Numeric {
    func indexSum() -> [Element] {
        return self.reduce([Element](), {acc, element in
            return acc + [(acc.last ?? 0) + element]
        })
    }
}

myArray.indexSum()  //[1, 3, 6, 10, 15]

以下是一种也适用于字符串的解决方案:

extension Array where Element == String {
    func indexSum() -> [String] {
        return self.reduce(into: [String]()) {(accumulator, element) in
            accumulator += [(accumulator.last ?? "") + element]
        }
    }
}

["a", "b", "c", "d"].indexSum() //["a", "ab", "abc", "abcd"]

如果您希望在初始数组元素的元素之间使用分隔符,则可以使用以下扩展名:

extension Array where Element == String {
    func indexSum(withSparator: String) -> [String] {
        return self.reduce(into: [String]()) {(accumulator, element) in
            var previousString = ""
            if let last = accumulator.last {
                previousString = last + " "
            }
            accumulator += [previousString +  element]
        }
    }
}

["a", "b", "c", "d"].indexSum(withSparator: " ") //["a", "a b", "a b c", "a b c d"]