我有一个代表虚拟轮播的项目数组。
const carousel = ['a','b','c','d','e'];
let currentIndex = 0;
function move (amount) {
const l = items.length; // in case carousel size changes
// need to update currentIndex
return carousel[currentIndex];
}
当 currentIndex == 0
时向左移动和当 currentIndex == length-1
时向右移动有什么干净或聪明的方法?
我以前考虑过这个问题,但从来没有想出任何非常聪明或简洁的东西。
最佳答案
简答
实现 circular array通过modular arithmetic .给定移动距离,计算相应的索引:
// put distance in the range {-len+1, -len+2, ..., -1, 0, 1, ..., len-2, len-1}
distance = distance % len
// add an extra len to ensure `distance+len` is non-negative
new_index = (index + distance + len) % len
长答案
使用modular arithmetic很像您阅读典型模拟时钟的方式。前提是两个整数相加,除以一个整数,保留余数。例如,13 = 3 (mod 10)
因为13
是1*10 + 3
和 3
是0*10 + 3
.
但为什么我们选择安排3
和 13
像我们一样?为了回答这个问题,我们考虑 Euclidean division algorithm (EDA) .它表示两个整数 a
和 b
存在唯一整数 q
和 r
这样
a = b*q + r
与 0 ≤ r < b
.这比您想象的更强大:它允许我们“以模数 n 工作”。
也就是说,我们可以说 a = b (mod n)
iff 存在唯一整数 q1
, r1
, q2
, 和 r2
这样
a = n * q1 + r1, 0 ≤ r1 < n
b = n * q2 + r2, 0 ≤ r2 < n
和r1
等于 r2
.我们调用r1
和 r2
“余数”。
回到前面的例子,我们现在知道为什么13 = 3 (mod 10)
了。 . EDA 说 13 = 1*10 + 3
那1
和 3
是唯一的q
和 r
满足必要的约束;按照类似的逻辑,3 = 0*10 + 3
.由于余数相等,我们说 13
和 3
在“working mod 10”时是相等的。
幸运的是,JavaScript 实现了一个 modulo operator天生的。不幸的是,我们需要注意一个怪癖,即模运算符保留其操作数的符号。这会给你一些结果,比如 -6 % 5 == -1
和 -20 % 7 == -6
.虽然完全有效的数学陈述(检查原因),但在涉及数组索引时这对我们没有帮助。
引理 1: a + n = a (mod n)
引理 2: -1 = n-1 (mod n)
引理 3: -a = n-a (mod n)
克服这个问题的方法是“欺骗”JavaScript 使用正确的符号。假设我们有一个长度为 len
的数组和当前索引 index
;我们想将索引移动一段距离 d
:
// put `d` within the range {-len+1, -len+2, ..., -2, -1, -0}
d = d % len
// add an extra len to ensure `d+len` is non-negative
new_index = (index + d + len) % len
我们首先将 d
在 {-len+1, -len+2, ..., -2, -1, -0}
范围内.接下来,我们添加一个额外的 len
确保我们移动的距离在 {1, 2, ..., len-1, len}
范围内,从而确保 %
的结果操作有一个积极的迹象。我们知道这是有效的,因为 (-a+b) + a = b (mod a)
.然后我们将新索引设置为 index + d + len (mod len)
.
更详细的实现:
class Carousel {
// assumes `arr` is non-empty
constructor (arr, index = 0) {
this.arr = arr
this.index = index % arr.length
}
// `distance` is an integer (...-2, -1, 0, 1, 2, ...)
move (distance) {
let len = this.arr.length
distance = distance % len
let new_index = (this.index + distance + len) % len
this.index = new_index
return this.arr[this.index]
}
}
// usage:
let c = new Carousel(['a','b','c','d','e'], 1) // position pointer set at 'b'
c.move(-1) // returns 'a' as (1 + -1 + 5) % 5 == 5 % 5 == 0
c.move(-1) // returns 'e' as (0 + -1 + 5) % 5 == 4 % 5 == 4
c.move(21) // returns 'a' as (4 + 21 + 5) % 5 == 30 % 5 == 0
关于javascript - 如何用数组实现轮播,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41274905/