简短而清晰斯威夫特 3 和 4我想出的解决方案:
extension Array {
func shifted(by shiftAmount: Int) -> Array<Element> {
// 1
guard self.count > 0, (shiftAmount % self.count) != 0 else { return self }
// 2
let moduloShiftAmount = shiftAmount % self.count
let negativeShift = shiftAmount < 0
let effectiveShiftAmount = negativeShift ? moduloShiftAmount + self.count : moduloShiftAmount
// 3
let shift: (Int) -> Int = { return $0 + effectiveShiftAmount >= self.count ? $0 + effectiveShiftAmount - self.count : $0 + effectiveShiftAmount }
// 4
return self.enumerated().sorted(by: { shift($0.offset) < shift($1.offset) }).map { $0.element }
}
}
解释:
- 没有元素且移位产生的标识的数组
立即返回原始数组
- 为了获得有效的移位量,无论函数传递的量如何,我们都会进行一些模计算,以消除会多次旋转数组中的元素的移位(例如,在具有 5 个对象的数组中,移位 +7与 +2 的移位相同)。由于我们总是想向右移位,为了用一个简单的函数而不是两个函数来完成,必须处理负输入(例如,在具有 5 个对象的数组中,移位 -2 与移位相同) +3)。因此,我们通过数组的长度来调整模计算的负结果。
当然,这三行可以写成一行,但我想让它尽可能地可读。
- 现在我们通过索引(
$0
) 的元素,并通过添加步骤 2 中计算的数量来返回移位后的索引。如果新索引超出数组长度,则需要回绕到前面。
- 最后,我们通过一些技巧将所有准备工作应用于我们的数组:
enumerated()
给我们一个元组数组[(offset: Int, element: Int)]
,它只是每个元素和元素本身的原始索引。然后我们通过操作的值对这个枚举数组进行排序offset
(又名元素的索引)通过应用步骤 3 中的函数。最后,我们通过将排序后的元素映射回数组来摆脱枚举。
此扩展适用于任何类型的数组。例子:
let colorArray = [
UIColor.red,
UIColor.orange,
UIColor.yellow,
UIColor.green,
UIColor.blue
]
let shiftedColorArray = [
UIColor.green,
UIColor.blue,
UIColor.red,
UIColor.orange,
UIColor.yellow
]
colorArray.shifted(by: 2) == shiftedColorArray // returns true
[1,2,3,4,5,6,7].shifted(by: -23) // returns [3,4,5,6,7,1,2]