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
有一个),其中第二个函数是一个选择器,或者你可以称之为谓词。如果您检查代码,您会发现我不能方便地组合使用 forEach
和 filter
,因为我需要知道我在原始数组中的位置。我试过谷歌搜索,但在任何地方都没有发现 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);
}
});
我最近写了下面的代码:
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
有一个),其中第二个函数是一个选择器,或者你可以称之为谓词。如果您检查代码,您会发现我不能方便地组合使用 forEach
和 filter
,因为我需要知道我在原始数组中的位置。我试过谷歌搜索,但在任何地方都没有发现 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);
}
});