使用额外的回调参数到Array.prototype.some

时间:2014-10-18 02:08:36

标签: javascript api

对于Array.prototype.someArray.prototype.any的回调,您是否有使用第二个和第三个参数的实际示例?

According to MDN

  使用三个参数调用

回调:元素的值,索引的索引   元素和正在遍历的Array对象。

我个人从未使用过它们。

我已经在Javascript函数式编程库Ramda上工作了一段时间,早期我们使controversial decision不使用我们创建的其他类似函数的索引和数组参数。这有很好的理由,我不需要进入这里,除了说某些功能,例如mapfilter,我们发现这些额外的参数确实有一些偶尔的效用。所以我们提供第二个功能,将它们提供给你的回调。 (例如,map.idx(yourFunc, list)。)

但我甚至从未考虑过someevery这样做。我从未想过这些实际用途。但现在有一个建议是我们将这些函数包含在支持索引的列表中。

所以我的问题是你是否曾经找到实际需要这些参数的someevery实际的,实时的,现实世界的回调函数?如果是这样,你能描述一下吗?

答案"不,我从来没有,"也是有用的数据,谢谢。

3 个答案:

答案 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. 如果输入是[1,2,3]
    1. 1添加到[2,3]的每个排列。
    2. 2添加到[1,3]的每个排列。
    3. 3添加到[1,2]的每个排列。
  3. 当然,该函数是递归的。在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函数的listpermutate参数。所以,是的,有些情况下这些额外的参数确实有用。

    然而,它们也存在问题

    然而,这些多余的论据有时会出问题并且难以调试。此问题行为的最常见示例是mapparseInt一起使用时:javascript - Array#map and parseInt

    &#13;
    &#13;
    alert(["1","2","3"].map(parseInt));
    &#13;
    &#13;
    &#13;

    如您所见,它会产生意外的输出[1,NaN,NaN]。发生这种情况的原因是因为map函数使用3个参数调用parseIntitemindexarray):

    parseInt("1", 0, ["1","2","3"]) // 1
    parseInt("2", 1, ["1","2","3"]) // NaN
    parseInt("3", 2, ["1","2","3"]) // NaN
    

    但是,parseInt函数需要2个参数(stringradix):

    1. 第一种情况,基数为0,即false。因此,采用默认基数10,得到1
    2. 第二种情况,基数为1。没有基础1数字系统。因此我们得到NaN
    3. 第三种情况,基数为2,有效。但是,基数2中没有3。因此我们得到NaN
    4. 如您所见,多余的参数可能会导致许多难以调试的问题。

      但是,还有另一种选择

      所以这些额外的参数很有用,但它们可能会导致很多问题。幸运的是,这个问题很容易解决。

      在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
      

      同样,您可以创建decorateArraydecorateIndicesArray个功能:

      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中,您有两个单独的函数mapmap.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));
      

      但是我们删除了多余的参数,因为它们引起了问题。我们为什么要重新添加它们?有两个原因:

      1. 看起来更干净。
      2. 有时你有一个由其他人编写的函数,希望这些额外的参数。
      3. 在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));
        

        这段代码很棒,因为:

        1. 每个功能只能完成一项工作。可以将功能组合起来进行更复杂的工作。
        2. 一切都是明确的,根据Zen of Python,这是一件好事。
        3. 没有冗余。只有一个map功能等
        4. 所以我真的希望这个答案有所帮助。