我想出的短而清晰的 Swift 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)。因此我们通过数组的长度来调整模计算的负结果。
当然,这 3 行可以合二为一,但我想让它尽可能可读。
- 现在我们通过获取元素的索引 (
$0) 并通过添加在步骤 2 中计算的数量返回移位的索引来准备实际移位。如果新索引落在数组长度之外,则需要对其进行包装绕到前面。
- 最后,我们通过一些技巧将所有准备工作应用于我们的数组:
enumerated() 为我们提供了一个元组数组[(offset: Int, element: Int)],它只是每个元素的原始索引和元素本身。然后,我们通过应用第 3 步中的函数,通过操纵的offset(也就是元素的索引)对这个枚举数组进行排序。最后,我们通过将排序后的元素映射回数组来摆脱枚举。
此扩展适用于任何类型的数组。例子:
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]