是否使用闭包,John Conway 的 Game of Life

To use closures or not, John Conways Game of Life

我目前正在制作一款游戏来说明 John Conways 'Game of Life' 我一直在学习闭包和模块化模式,并且正在尝试实现更多我在 Javascript 中学到的东西。 You can check out my repo here if your interested. 所有这些方法都在一个 'Game' 对象中。我让它工作,但我试图限制变量并使其尽可能发挥作用(在函数之间传递网格数组)。我想要一些关于我应该保持更高范围的变量不可变还是无关紧要的建议。

就结构而言,哪种解决方案更好?

// initial grid
// will be bigger than this but is 3x3 for this question.

let grid = [
    [1,0,0],
    [0,1,0],
    [0,0,1]
]

解法一:我最初的解法:

function nextGrid(){
  grid = applyRules(grid)
  renderGrid(grid)
}

function applyRules(grid){
  // makes changes
}

function renderGrid(grid){
  // makes changes
}

// call nextGrid() to generate subsequent new grids.

解决方案 2: 仔细研究了闭包,这样更好吗?

function nextGrid() {
  let prevGrid = grid;

  return function(){
    prevGrid = applyRules(prevGrid)
    renderGrid(prevGrid)
  }  
}

function applyRules(prevGrid){
  // makes changes
}

function renderGrid(grid){
  // makes changes
}

const next = nextGrid();

// call next() to generate subsequent new grids.

你的两个解决方案都采用了突变。在您的第一个解决方案中,您改变了全局变量 grid。在你的第二个解决方案中,你改变了局部变量 prevGrid。一个真正的功能性程序将使用递归而不是变异。例如,这就是我要做的:

const game = /* initial game state */;

play(game);

function play(game) {
    render(game);
    setTimeout(play, 100, update(game));
}

如您所见,我们每 100 毫秒用更新的游戏状态调用 play,而不是改变全局变量 game。我们在第三行的初始调用 play(game) 开始游戏。如果我们把它注释掉,什么也不会显示。


下面是 Conway's Game of Life 的完整示例,仅需几行代码:

const glyph = [" ", "■"];

document.querySelectorAll("pre.life").forEach(e => play(e, read(e.innerHTML)));

function read(text) {
    return text.split("\n").map(l => l.split("").map(c => glyph.indexOf(c)));
}

function play(pre, game) {
    pre.innerHTML = show(game);
    setTimeout(play, 100, pre, update(game));
}

function show(game) {
    return game.map(line => line.map(cell => glyph[cell]).join("")).join("\n");
}

function update(game) {
    return game.map((line, i) => line.map((cell, j) => {
        const back = game[i - 1], next = game[i + 1], h = j - 1, k = j + 1;

        const neighbors = (back && back[h] || 0)
                        + (back && back[j] || 0)
                        + (back && back[k] || 0)
                        + (line[h] || 0)
                        + (line[k] || 0)
                        + (next && next[h] || 0)
                        + (next && next[j] || 0)
                        + (next && next[k] || 0);

        switch (neighbors) {
        case 3:  return 1;
        case 2:  return cell;
        default: return 0;
        }
    }));
}
pre.life {
    line-height: 0.666;
}
<pre class="life">
                                      
                         ■            
                       ■ ■            
             ■■      ■■            ■■ 
            ■   ■    ■■            ■■ 
 ■■        ■     ■   ■■               
 ■■        ■   ■ ■■    ■ ■            
           ■     ■       ■            
            ■   ■                     
             ■■                       
                                      
                                      
                                      
                                      
                                      
                                      
                                      
                                      
                                      
                                      
                                      
                                      
                                      
                                       </pre>

上面的模式就是Gosper glider gun.