Swift:循环遍历数组元素并访问上一个和下一个元素

x759pob2  于 2022-10-31  发布在  Swift
关注(0)|答案(7)|浏览(144)

在Swift中,我想循环遍历一个数组,并将每个元素与前一个和/或下一个元素进行比较。对于每次比较,我要么产生一个新元素,要么什么都不产生。有没有“功能性”的方法来做这件事?
例如,我有一个Int的数组,想找到所有的“局部最小值”。
我可以像这样依次做

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
var i = 1
var r: [Int] = []

while i < a.count - 1 {
    if a[i] < a[i+1] && a[i] < a[i-1] {
        r.append(i)
    }
    i += 1
}

print(r)
// [6, 11]

我想知道是否有更简单或更直接的方法来做这件事。

5cg8jx4n

5cg8jx4n1#

一般来说,可以使用dropFirst()zip()并行遍历相邻的数组元素。下面是一个简单的例子,它生成数组元素之间的增量数组:

let a = [ 1, 2, 2, 3, 5, 4, 2, 5, 7, 9, 5, 3, 8, 10 ]

let diffs = zip(a.dropFirst(), a).map(-)
print(diffs)
// [1, 0, 1, 2, -1, -2, 3, 2, 2, -4, -2, 5, 2]

为了计算局部最小值的索引,我们可以并行地迭代aa.dropFirst()a.dropFirst(2)enumerated()用于跟踪数组偏移,并且flatMap()(在Swift 4.1中重命名为compactMap())用于仅选取对应于局部最小值的那些索引:

let a = [ 1, 2, 2, 3, 5, 4, 2, 5, 7, 9, 5, 3, 8, 10 ]

let localMins = zip(a.enumerated().dropFirst(), zip(a, a.dropFirst(2))).flatMap {
    $0.element < $1.0 && $0.element < $1.1 ? $0.offset : nil
}
print(localMins) // [6, 11]
dxxyhpgq

dxxyhpgq2#

您可以将while循环和i替换为for循环和stride

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
var r: [Int] = []

for i in stride(from: 1, to: a.count - 1, by: 1) {
    if a[i] < a[i+1] && a[i] < a[i-1] {
        r.append(i)
    }
}

print(r)
// [6, 11]

你可以使用一个过滤器,但它的可读性不如上面的代码:

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
let r = a.enumerated().dropFirst().dropLast().filter { $0.1 < a[$0.0 + 1] && $0.1 < a[$0.0 - 1] }.map { $0.0 }
print(r)
// [6, 11]
0ejtzxu1

0ejtzxu13#

我在寻找原始Q的一个变体,希望它能对其他人有所帮助。我需要Map数组中的每一项,同时考虑前面和后面的值:

extension Sequence {
    var withPreviousAndNext: [(Element?, Element, Element?)] {
        let optionalSelf = self.map(Optional.some)
        let next = optionalSelf.dropFirst() + [nil]
        let prev = [nil] + optionalSelf.dropLast()
        return zip(self, zip(prev, next)).map {
            ($1.0, $0, $1.1)
        }
    }
}

用这个词搭配原始Q的方式不太好:

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
let indices = a.enumerated().withPreviousAndNext.compactMap { values -> Int? in
    let (prev, cur, next) = values
    return (cur.1 < (prev?.1 ?? Int.min) && cur.1 < (next?.1 ?? Int.min)) ? cur.0 : nil
}
indices // [6,11]
wnvonmuf

wnvonmuf4#

使用flatMap

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
let r = a.enumerated().flatMap { (_ offset: Int, _ element: Int) -> Int? in
    guard offset > 0 else { return nil }
    if element < a[offset-1] && element < a[offset+1] {
        return offset
    }
    return nil
}
06odsfpq

06odsfpq5#

您还可以迭代索引并进行如下比较,

for i in a.indices.dropFirst().dropLast()
{
    if a[i] < a[a.index(after: i)],
            a[i] < a[a.index(before: i)] {
        r.append(i)
    }
}
print(r)
// [6, 11]

或者,类似这样的东西,

let result = a.indices.dropLast().dropFirst().filter { i in
    return a[i] < a[a.index(after: i)] &&
            a[i] < a[a.index(before: i)]
}
print(r)
// [6, 11]

或者,短,

let result = a.indices.dropLast()
                      .dropFirst()
                      .filter { a[$0] < a[$0 + 1] &&
                                a[$0] < a[$0 - 1] }
 print(result)
i86rm4rw

i86rm4rw6#

一个简单的for循环是否具有足够的可读性和可维护性?你可以在迭代时缓存中间值,这样你在每次迭代中只访问数组中的一个元素。如果你想将其推广到任何类似的类型,你可以将其实现为Array的扩展:

extension Array where Element: Comparable {

    func localMinimums() -> [Int] {
        var minimums = [Int]()

        var currentValue = self[0]
        var nextValue = self[1]
        for index in 1..<(self.count - 1) {
            let previousValue = currentValue
            currentValue = nextValue
            nextValue = self[index + 1]
            if currentValue < nextValue && currentValue < previousValue {
                minimums.append(index)
            }
        }

        return minimums
    }
}

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
let r = a.localMinimums()
print(r)
// [6, 11]
crcmnpdw

crcmnpdw7#

我认为马丁·R的回答很聪明,尽管我试着用另一种方式回答。

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]

func withPrevAndNext<T, U>(`default`: U, _ f: @escaping (T, T, T) -> (U)) -> (T) -> (U) {
    var previous: T?
    var current: T?

    return { next in
        defer { (previous, current) = (current, next) }
        guard let prev = previous, let curt = current else { return `default` }
        return f(prev, curt, next)
    }
}

let r = a.enumerated().compactMap(withPrevAndNext(default: .none) { prev, curt, next -> Int? in
    curt.1 < prev.1 && curt.1 < next.1 ? curt.0 : .none
})

print(r)
// [6, 11]

相关问题