我正在寻找一个收藏夹的“反向拉链”。本质上意义:
let a = [1, 2, 3, 4, 5, 6, 7]
let (left, right) = a.reverseZip()
print(left) // [1, 3, 5, 7]
print(right) // [2, 4, 6]
我有一个似乎可行的实现(请参阅下面的答案),但是我有两个问题希望我们可以讨论:
zip
并非完全相反(我敢肯定,这种模式在另一种功能语言的其他地方也存在)答案 0 :(得分:2)
这是一个想法的种子。而不是返回数组,返回序列会更有效。
这是一个Array
扩展名,它返回两个LazyMapSequence<StrideTo<Int>, Element>
。好处是实际上不会生成序列。因为它们是lazy
,所以按需提供它们。这很像reversed()
在Array
上的工作方式。
extension Array {
func reverseZip() -> (LazyMapSequence<StrideTo<Int>, Element>, LazyMapSequence<StrideTo<Int>, Element>) {
let left = stride(from: 0, to: self.count, by: 2).lazy.map { self[$0] }
let right = stride(from: 1, to: self.count, by: 2).lazy.map { self[$0] }
return (left, right)
}
}
示例:
let a = [1, 2, 3, 4, 5, 6, 7]
let (left, right) = a.reverseZip()
// iterate left
for i in left {
print(i)
}
1 3 5 7
// turn them into arrays to print them
print(Array(left))
[1, 3, 5, 7]
print(Array(right))
[2, 4, 6]
我确信可以将其推广到比Array
更通用的地方。留给读者练习。
如果要两个数组
如果您确实需要两个数组,则只需返回map
的结果(取出lazy
):
extension Array {
func reverseZip() -> ([Element], [Element]) {
let left = stride(from: 0, to: self.count, by: 2).map { self[$0] }
let right = stride(from: 1, to: self.count, by: 2).map { self[$0] }
return (left, right)
}
}
答案 1 :(得分:1)
extension Collection {
func reverseZip() -> ([Element], [Element]) {
var left = [Element]()
var right = [Element]()
let capacity = Double(self.count) / 2.0
left .reserveCapacity(Int(ceil(capacity)))
right.reserveCapacity(self.count / 2)
for element in self {
if left.count > right.count {
right.append(element)
} else {
left.append(element)
}
}
return (left, right)
}
}
更新(18/10/18)
谢谢大家的帮助。似乎很多人都陷入了Stride
,这绝对是一个好方法。我拒绝此方法有两个原因:
Collection
的扩展名,而不是Array
startIndex
的{{1}}不一定为0,索引的进程不一定每次都增加1。Martin R使用自定义迭代器的方法绝对看起来是正确的方法,因此我将其与WWDC 2018's implementation of "EveryOther"一起使用,以创建一个标准的顺序迭代器,该迭代器仅在每个while循环块中遍历两个元素,直到结束了。
Collection
毫无疑问,在一定程度上可以进一步改进并使用vacawama的extension Collection {
func deinterleave() -> ([Element], [Element]) {
let smallHalf = count / 2
let bigHalf = count - smallHalf
var left = [Element]()
var right = [Element]()
left .reserveCapacity(bigHalf)
right.reserveCapacity(smallHalf)
let start = self.startIndex
let end = self.endIndex
var iter = start
while iter != end {
left.append(self[iter])
iter = index(after: iter)
if iter == end { break }
right.append(self[iter])
iter = index(after: iter)
}
return (left, right)
}
}
let a = [1,2,3,4,5,6,7]
print(a.deinterleave()) // ([1, 3, 5, 7], [2, 4, 6])
实现,但是就目前而言,这已经满足了我的需要。
答案 2 :(得分:1)
基于vacawama的想法,即返回惰性集合而不是数组, 这是对具有任意跨度的任意集合的可能概括:
extension Collection {
func makeStrideIterator(stride: Int) -> AnyIterator<Element> {
precondition(stride > 0, "The stride must be positive")
var it = makeIterator()
var first = true
return AnyIterator<Element> {
if first {
first = false
} else {
// Skip `stride - 1` elements:
for _ in 1..<stride {
guard let _ = it.next() else { return nil }
}
}
return it.next()
}
}
}
该方法返回一个AnyIterator
(既是迭代器又是序列)
位置的收集元素
0, stride, 2*stride, ...
解压缩数组(或任何其他集合)可以通过
完成。let a = [1, 2, 3, 4, 5]
let left = a.makeStrideIterator(stride: 2)
let right = a.dropFirst().makeStrideIterator(stride: 2)
for e in left { print(e) }
// 1, 3, 5
for e in right { print(e) }
// 2, 4
以下是从字符串中提取第三个字符的示例:
for c in "ABCDEFG".makeStrideIterator(stride: 3) {
print(c)
}
// A D G
然后将解压缩为两个数组的特殊情况实现为
extension Collection {
func unzip() -> ([Element], [Element]) {
return (Array(makeStrideIterator(stride: 2)),
Array(dropFirst().makeStrideIterator(stride: 2)))
}
}
示例:
print([1, 2, 3, 4, 5].unzip())
// ([1, 3, 5], [2, 4])