我有一系列代表虚拟轮播的项目。
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
时向右移动时,处理向左移动的干净或巧妙方法是什么?
我之前已经考虑过这个问题,并且从未有过任何非常聪明或简洁的事情。
答案 0 :(得分:8)
通过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
。这比你想象的更强大:它允许我们“以模数运算”。
也就是说,我们可以说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
。由于余数相等,我们说当“工作模式10”时13
和3
相等。
幸运的是,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
答案 1 :(得分:1)
currentIndex = currentIndex + change;
if (currentIndex >= l) currentIndex = 0;
if (currentIndex < 0) currentIndex = l - 1;
这将修改索引,检查它是否已破坏可能的值并调整为“&#39; side&#39;旋转木马。
答案 2 :(得分:1)
前一段时间我实施了Array.prototype.rotate()
。这项工作可能非常方便。这是代码;
Array.prototype.rotate = function(n) {
var len = this.length;
return !(n % len) ? this.slice()
: this.map((e,i,a) => a[(i + (len + n % len)) % len]);
};
var a = [1,2,3,4,5,6,7,8,9],
b = a.rotate(10);
console.log(JSON.stringify(b));
b = a.rotate(-10);
console.log(JSON.stringify(b));