JavaScript forEach with selector -- JavaScript if 语句的功能替代

JavaScript forEach with selector -- functional replacement for JavaScript if statement

我最近写了下面的代码:

function genBoard(width, height, numMines) {
  let mineField = _.shuffle(Array(numMines).fill('*').concat(Array(width*height - numMines).fill(0)));
  mineField.forEach((elem, pos) => {
    if (elem == '*') {
      let blastArea = [pos-width-1, pos-width, pos-width+1, pos-1, pos+1, pos+width-1, pos+width, pos+width+1];
      blastArea.filter(p => p>=0 && p<width*height && mineField[p] != '*').forEach(p => mineField[p] += 1);
    }
  });
  _.chunk(mineField, width).forEach(row => console.log(row.join('')));
}

请注意,我使用的是 lodash 中的 _.shuffle_.chunk

我想以尽可能实用的方式来做这件事。所以我希望中间部分更像这样:

mineField.forEachFiltered((elem, pos) => {
  let blastArea = [pos-width-1, pos-width, pos-width+1, pos-1, pos+1, pos+width-1, pos+width, pos+width+1];
  blastArea.filter(p => p>=0 && p<width*height && mineField[p] != '*').forEach(p => mineField[p] += 1);
  },
  elem => elem == '*'
);

换句话说,forEachFiltered 有两个函数(而不是 forEach 有一个),其中第二个函数是一个选择器,或者你可以称之为谓词。如果您检查代码,您会发现我不能方便地组合使用 forEachfilter,因为我需要知道我在原始数组中的位置。我试过谷歌搜索,但在任何地方都没有发现 forEachFiltered 的任何概念,而且在 lodash 中也没有。我必须编写该函数还是有其他方法(或仅使用 if 语句)?

P.S.: 示例输出值 10, 6, 24:

*2233222**
32****3*6*
312445*5**
5322*4*5**
***22*35**
432112*3*3

P.P.S.: 以最简单的方式问我的问题,你好["my", "array", "of", "whatever"].forEach(func, selector) ?

我还没有看到任何 javascript 库具有像您建议的那样工作的迭代函数(通过在正常函数之外采用谓词函数)。另一方面,Common Lisp 具有类似 substitute-if (http://www.lispworks.com/documentation/HyperSpec/Body/f_sbs_s.htm#substitute-if) 的函数(实际上有几个可选参数进一步完善了它们的行为),但即便如此它也没有任何东西很像你的 forEachFiltered 功能。一方面,与 JS 不同,Common Lisp 迭代器函数根本不传递任何位置。

无论如何,在你的具体情况下,我会说你可以 map 在板上生成一个对象列表,每个对象代表一个单元格,包含单元格的内容以及单元格在雷区。然后,您可以 filter 生成仅包含地雷的那些单元格的列表,并在其上使用 forEach 来计算邻接关系。这与使用您正在考虑的 forEachFiltered 函数不太一样,但允许您做您想做的事。你可能会反对这样传递坐标感觉很奇怪,事实上它可能是。但是我要说的是,它正是您开始使用的嵌套循环的对偶。

另一种替代方法是编写模仿 Javascript 的本机迭代方法的迭代函数,但是当您遍历 n-dimensional 数组时,它会传递给您广义的 n-dimensional 坐标。那将是一个不错的小图书馆。

根据 polyfill for Array.prototype.forEach.

编写您自己的 forEachFiltered 实现相当简单
Object.defineProperty(Array.prototype, 'forEachFiltered', {
    value: function (func, predicate, thisArg) {
        var array = Object(this),
            index = 0,
            length = array.length >>> 0,
            value;

        while (index < length) {
            if (index in array) {
                value = array[index];
                if (predicate.call(thisArg, value, index, array)) {
                    func.call(thisArg, value, index, array);
                }
            }
            index++;
        }
    }
});

或者您可以将其实现为 Array.prototype.forEach:

的包装器
Object.defineProperty(Array.prototype, 'forEachFiltered', {
    value: function (func, predicate, thisArg) {
        this.forEach(function () {
            if (predicate.apply(this, arguments)) {
                func.apply(this, arguments);
            }
        }, thisArg);
    }
});