如何使用数组实现轮播

Ale*_*lls 4 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];

}
Run Code Online (Sandbox Code Playgroud)

什么是干净或聪明的方式来处理向左currentIndex == 0移动和向右移动时currentIndex == length-1

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

roy*_*wie 9

简短的回答

通过模运算实现循环数组.给定移动距离,计算适当的指数:

// 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
Run Code Online (Sandbox Code Playgroud)

答案很长

使用模块化算法非常类似于您阅读典型模拟时钟的方法.前提是添加两个整数,除以整数,并保留余数.例如,13 = 3 (mod 10)因为131*10 + 330*10 + 3.

但为什么我们选择安排313我们一样呢?为了回答这个问题,我们考虑欧几里德分割算法(EDA).它说了两个整数ab存在唯一的整数q,并r使得

a = b*q + r
Run Code Online (Sandbox Code Playgroud)

0 ? r < b.这比你想象的更强大:它允许我们"以模数运算".

也就是说,我们可以说,a = b (mod n) 当且仅当有独特的整数q1,r1,q2,和r2这样

a = n * q1 + r1,   0 ? r1 < n
b = n * q2 + r2,   0 ? r2 < n
Run Code Online (Sandbox Code Playgroud)

r1平等r2.我们把r1r2的"余".

回到前面的例子,我们现在知道原因了13 = 3 (mod 10).EDA说13 = 1*10 + 3,1而且3是唯一qr令人满意的必要限制; 通过类似的逻辑,3 = 0*10 + 3.由于余数相等,我们在"工作模式10"时说13并且3相等.

幸运的是,JavaScript 本身实现了模运算符.不幸的是,我们需要注意一个怪癖,即模运算符保持其操作数的符号.这给你一些像-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
Run Code Online (Sandbox Code Playgroud)

我们通过首先放入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
Run Code Online (Sandbox Code Playgroud)