Swift数组分割成重叠块

fykwrbwg  于 2023-04-28  发布在  Swift
关注(0)|答案(3)|浏览(132)

我在想怎么把数组分割成重叠的块。
作为示例,这是输入:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
这是预期的输出(块大小为3):
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
我开始使用stride(通过Hacking With Swift):

extension Array {
    func chunked(into size: Int) -> [[Element]] {
        return stride(from: 0, to: count, by: size).map {
            Array(self[$0 ..< Swift.min($0 + size, count)])
        }
    }
}

这当然给出了这个不正确的输出:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
我找不到stride的任何变体来获得预期的结果,所以就这样做了,使用两个嵌套的for-loops

extension Array {
    func chunked2(into size: Int) -> [[Element]] {
        var result: [[Element]] = []
        
        for i in 0...self.count {
            var chunk: [Element] = []
           
            for j in i..<i+size {
                if j < self.count {
                    chunk.append(self[j])
                }
            }
        
            if chunk.count == size {
                result.append(chunk)
            }
        }
        
        return result
    }
}

这是可行的,并给出了预期的结果。
问:我想知道我是否可以用一种更“敏捷”的方式重写它,使用像stridemap,也许reduce或其他我缺少的函数?

6uxekuva

6uxekuva1#

你所描述的就是所谓的“窗口”。“它在数字信号处理中非常常见,并且在swift-algorithms中开箱即用。有关swift-algorithms包及其在Swift生态系统中的作用的更多信息,请参阅Announcing Swift Algorithms
如果你想看一个算法如何工作的例子,请查看the source。他们使用数据结构WindowsOfCountCollection来实现它,而不是创建一堆zip等。这通常更有效,因为窗口是按需生成的,并且不必预先分配大量内存。

72qzrwbm

72qzrwbm2#

我想知道我是否可以用一种更“迅捷”的方式重写它,使用像stride,map,reduce或其他我缺少的功能?“**.
是的,您可以使用集合func sequence<T, State>(state: State, next: @escaping (inout State) -> T?) -> UnfoldSequence<T, State>方法创建自己的方法,如下所示:

extension Collection {
    func windows(of count: Int) -> UnfoldSequence<SubSequence,Index> {
        sequence(state: startIndex) { start in
            guard start < endIndex,
                let end = index(
                    start,
                    offsetBy: count,
                    limitedBy: endIndex
                )
            else { return nil }
            defer { formIndex(after: &start) }
            return self[start..<end]
        }
    }
}

使用方法:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let windows = numbers.windows(of: 3)
for window in windows {
    print(window)
}

这将打印:
[一、二、三]
[二、三、四]
[3、4、5]
[4、5、6]
[5、6、7]
[6、7、8]
[7、8、9]
[8、9、10]
请注意,结果序列的元素是惰性计算的。它将返回一个UnfoldSequence,而不复制原始元素。
如果你需要输出一个子序列数组,你可以这样做:

extension Collection {
    func windows(of count: Int) -> [SubSequence] {
        .init(sequence(state: startIndex) { start in
            guard start < endIndex,
                let end = index(
                    start,
                    offsetBy: count,
                    limitedBy: endIndex
                )
            else { return nil }
            defer { formIndex(after: &start) }
            return self[start..<end]
        })
    }
}

如果你真的需要复制结果元素并输出数组的数组:

extension Collection  {
    func windows(of count: Int) -> [[Element]] {
        .init(sequence(state: startIndex) { start in
            guard start < endIndex,
                let end = index(
                    start,
                    offsetBy: count,
                    limitedBy: endIndex
                )
            else { return nil }
            defer { formIndex(after: &start) }
            return .init(self[start..<end])
        })
    }
}
pepwfjgg

pepwfjgg3#

stride()或多或少是一个具有不同于1的增量的for循环。但是在这里,你想迭代每个元素(即,递增1),并将接下来的n个元素与之一起使用。
我发现这个问题很有趣,所以这里有“两个”其他方法。
数组有索引,所以你可以使用它们,有时,我们倾向于忘记它们(关于indices值,每次都使用someArray[index])。
我在第一个解决方案中使用compactMap()。这或多或少是一个简单的循环。它的一个小缺点是,如果你的数组有n个元素,它将迭代n次,而不是“n - size”。
我在第二个解决方案中使用map。它或多或少是一个简单的循环。我计算了before和end,避免了前面的解决方案中不必要的迭代。
如果需要的话,你可以把这两种想法结合起来。不清楚如何处理大小限制,如0、size〉count等,所以我保留了日志。

extension Array {
    func windows(size: Int) -> [[Element]] {
        indices.compactMap { anElement in
            let end = anElement.advanced(by: size)
            guard end <= count else { return nil }
            return Array(self[anElement ..< end])
        }
    }

    func windows2(size: Int) -> [[Element]] {
        guard size <= count else { return [] } //We check before, else, the next line will crash
        let end = indices.upperBound.advanced(by: -size)
        return (indices.lowerBound...end).map {
            Array(self[$0..<$0.advanced(by: size)])
        }
    }
}

游戏测试:

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

print("Window: 3") //"Normal" behavior
print(initialArray.windows(size: 3))
print(initialArray.windows2(size: 3))
print("Window: 10") //Size = count
print(initialArray.windows(size: 10))
print(initialArray.windows2(size: 10))
print("Window: 0") //No size
print(initialArray.windows(size: 0))
print(initialArray.windows2(size: 0))
print("Window: 1") //Size of 1
print(initialArray.windows(size: 1))
print(initialArray.windows2(size: 1))
print("Window: 11") //Window greater than the count
print(initialArray.windows(size: 11))
print(initialArray.windows2(size: 11))

输出:

Window: 3
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
Window: 10
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
Window: 0
[[], [], [], [], [], [], [], [], [], []]
[[], [], [], [], [], [], [], [], [], [], []] //Here there is a differenccec
Window: 1
[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
Window: 11
[]
[]

相关问题