对于Array.prototype.some
或Array.prototype.any
的回调,您是否有使用第二个和第三个参数的实际示例?
使用三个参数调用回调:元素的值,索引的索引 元素和正在遍历的Array对象。
我个人从未使用过它们。
我已经在Javascript函数式编程库Ramda上工作了一段时间,早期我们使controversial decision不使用我们创建的其他类似函数的索引和数组参数。这有很好的理由,我不需要进入这里,除了说某些功能,例如map
和filter
,我们发现这些额外的参数确实有一些偶尔的效用。所以我们提供第二个功能,将它们提供给你的回调。 (例如,map.idx(yourFunc, list)
。)
但我甚至从未考虑过some
或every
这样做。我从未想过这些实际用途。但现在有一个建议是我们将这些函数包含在支持索引的列表中。
所以我的问题是你是否曾经找到实际需要这些参数的some
或every
实际的,实时的,现实世界的回调函数?如果是这样,你能描述一下吗?
答案"不,我从来没有,"也是有用的数据,谢谢。
答案 0 :(得分:1)
快速搜索我们的代码:
function isAscending(array) {
return array.every(function (e, idx, arr) {
return (idx === 0) ? true : arr[idx-1] <= e;
});
}
答案 1 :(得分:1)
我可以想象类似下面的代码来检查数组是否是无副本的:
….every(function(v, i, arr) {
return arr.indexOf(v, i+1) == -1;
})
其中…
是一个复杂的表达式,所以你真的必须使用arr
参数 - 如果你正确地将自己的函数中的功能分解出去,这就不再是问题了。将数组作为参数。
第二个参数有时会有用,但我支持你很少使用它的立场。
答案 2 :(得分:1)
这些额外的参数实际上确实派上用场了,但并不常见。
在最近的过去,我编写了一个函数来查找元素列表的所有排列:
permute :: [a] -> [[a]]
例如permute [1,2,3]
将是:
[ [1,2,3]
, [1,3,2]
, [2,1,3]
, [2,3,1]
, [3,1,2]
, [3,2,1]
]
这个功能的实现非常简单:
[]
,则返回[[]]
。这是边缘情况。[1,2,3]
:
1
添加到[2,3]
的每个排列。2
添加到[1,3]
的每个排列。3
添加到[1,2]
的每个排列。当然,该函数是递归的。在JavaScript中,我实现如下:
var permute = (function () {
return permute;
function permute(list) {
if (list.length === 0) return [[]]; // edge case
else return list.reduce(permutate, []); // list of permutations
// is initially empty
}
function permutate(permutations, item, index, list) {
var before = list.slice(0, index); // all the items before "item"
var after = list.slice(index + 1); // all the items after "item"
var rest = before.concat(after); // all the items beside "item"
var perms = permute(rest); // permutations of rest
// add item to the beginning of each permutation
// the second argument of "map" is the "context"
// (i.e. the "this" parameter of the callback)
var newPerms = perms.map(concat, [item]);
return permutations.concat(newPerms); // update the list of permutations
}
function concat(list) {
return this.concat(list);
}
}());
如您所见,我使用了index
函数的list
和permutate
参数。所以,是的,有些情况下这些额外的参数确实有用。
然而,这些多余的论据有时会出问题并且难以调试。此问题行为的最常见示例是map
和parseInt
一起使用时:javascript - Array#map and parseInt
alert(["1","2","3"].map(parseInt));
&#13;
如您所见,它会产生意外的输出[1,NaN,NaN]
。发生这种情况的原因是因为map
函数使用3个参数调用parseInt
(item
,index
和array
):
parseInt("1", 0, ["1","2","3"]) // 1
parseInt("2", 1, ["1","2","3"]) // NaN
parseInt("3", 2, ["1","2","3"]) // NaN
但是,parseInt
函数需要2个参数(string
和radix
):
0
,即false
。因此,采用默认基数10,得到1
。1
。没有基础1数字系统。因此我们得到NaN
。2
,有效。但是,基数2中没有3
。因此我们得到NaN
。如您所见,多余的参数可能会导致许多难以调试的问题。
所以这些额外的参数很有用,但它们可能会导致很多问题。幸运的是,这个问题很容易解决。
在Haskell中,如果你想map
超过一个值列表和每个值的索引,那么你可以按如下方式使用它:
map f (zip list [0..])
list :: [Foo]
[0..] :: [Int]
zip list [0..] :: [(Foo, Int)]
f :: (Foo, Int) -> Bar
map f (zip list [0..]) :: [Bar]
您可以在JavaScript中执行相同的操作,如下所示:
function Maybe() {}
var Nothing = new Maybe;
Just.prototype = new Maybe;
function Just(a) {
this.fromJust = a;
}
function iterator(f, xs) {
var index = 0, length = xs.length;
return function () {
if (index < length) {
var x = xs[index];
var a = f(x, index++, xs);
return new Just(a);
} else return Nothing;
};
}
我们使用不同的map
函数:
function map(f, a) {
var b = [];
if (typeof a === "function") { // iterator
for (var x = a(); x !== Nothing; x = a()) {
var y = f(x.fromJust);
b.push(y);
}
} else { // array
for (var i = 0, l = a.length; i < l; i++) {
var y = f(a[i]);
b.push(y);
}
}
return x;
}
最后:
function decorateIndices(array) {
return iterator(function (item, index, array) {
return [item, index];
}, array);
}
var xs = [1,2,3];
var ys = map(function (a) {
var item = a[0];
var index = a[1];
return item + index;
}, decorateIndices(xs));
alert(ys); // 1,3,5
同样,您可以创建decorateArray
和decorateIndicesArray
个功能:
function decorateArray(array) {
return iterator(function (item, index, array) {
return [item, array];
}, array);
}
function decorateIndicesArray(array) {
return iterator(function (item, index, array) {
return [item, index, array];
}, array);
}
目前在Ramda中,您有两个单独的函数map
和map.idx
。上述解决方案允许您将map.idx
替换为idx
,以便:
var idx = decorateIndices;
var arr = decorateArray;
var idxArr = decorateIndicesArray;
map.idx(f, list) === map(f, idx(list))
这将允许您摆脱一大堆.idx
函数和变体。
还有一个小问题需要解决。这看起来很难看:
var ys = map(function (a) {
var item = a[0];
var index = a[1];
return item + index;
}, decorateIndices(xs));
能够像这样写它会更好:
var ys = map(function (item, index) {
return item + index;
}, decorateIndices(xs));
但是我们删除了多余的参数,因为它们引起了问题。我们为什么要重新添加它们?有两个原因:
在Haskell中,您可以使用uncurry
函数来解决此问题:
map (uncurry f) (zip list [0..])
list :: [Foo]
[0..] :: [Int]
zip list [0..] :: [(Foo, Int)]
f :: Foo -> Int -> Bar
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f :: (Foo, Int) -> Bar
map (uncurry f) (zip list [0..]) :: [Bar]
在JavaScript中,uncurry
函数只是apply
。它实现如下:
function uncurry(f, context) {
if (arguments.length < 2) context = null;
return function (args) {
return f.apply(context, args);
};
}
使用uncurry
我们可以将上面的例子写成:
var ys = map(uncurry(function (item, index) {
return item + index;
}), decorateIndices(xs));
这段代码很棒,因为:
map
功能等所以我真的希望这个答案有所帮助。