如何使用数组实现轮播

时间:2016-12-22 02:25:23

标签: javascript algorithm

我有一系列代表虚拟轮播的项目。

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时向右移动时,处理向左移动的干净或巧妙方法是什么?

我之前已经考虑过这个问题,并且从未有过任何非常聪明或简洁的事情。

3 个答案:

答案 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)131*10 + 330*10 + 3

但为什么我们选择像我们一样安排313?要回答这个问题,我们会考虑Euclidean division algorithm (EDA)。它表示对于两个整数ab,存在唯一的整数qr,以便

a = b*q + r

0 ≤ r < b。这比你想象的更强大:它允许我们“以模数运算”。

也就是说,我们可以说a = b (mod n) iff 有唯一的整数q1r1q2r2这样

a = n * q1 + r1,   0 ≤ r1 < n
b = n * q2 + r2,   0 ≤ r2 < n

r1等于r2。我们将r1r2称为“余数”。

回到上一个例子,我们现在知道为什么13 = 3 (mod 10)。 EDA说13 = 1*10 + 313是唯一满足必要约束的qr;通过类似的逻辑,3 = 0*10 + 3。由于余数相等,我们说当“工作模式10”时133相等。

幸运的是,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));