自定义迭代的功能方式

时间:2016-05-16 20:53:34

标签: javascript functional-programming

如何仅使用mapreducefilter或任何功能方式在阵列上创建自定义迭代?

假设我想将一个数组映射到另一个数组,该数组包含源数组中每三个相邻元素的总和:

var source = [1, 2, 3, 4, 6, 7, 8] // to [6, 17, 8]

或者制作两个元素:

var source = [1, 2, 3, 4, 5, 6, 7] // to [[1, 2], [3, 4], [5, 6], [7]]

对于第二个我有以下但是看起来不是很有用,因为我正在通过索引访问数组:

function* pairMap(data) {
    yield* data.map((item, index) => {
        if (index > 0) {
            return [data[index - 1], item];
        }
    });
}

我对这样做的功能方式很感兴趣。

3 个答案:

答案 0 :(得分:10)

  

假设我想将一个数组映射到另一个数组,该数组包含源数组中每三个相邻元素的总和:

var source = [1, 2, 3, 4, 6, 7, 8] // to [6, 17, 8]

地图创建1:1关系,因此这不适合map。相反,reduce或(“折叠”)在这里会更好。

const comp = f=> g=> x=> f (g (x));
const len = xs=> xs.length;
const isEmpty = xs=> len(xs) === 0;
const concat = xs=> ys=> ys.concat(xs);

const chunk= n=> xs=>
  isEmpty (xs)
    ? []
    : concat (chunk (n) (xs.slice(n))) ([xs.slice(0,n)]);

const reduce = f=> y=> xs=> xs.reduce((y,x)=> f(y)(x), y);
const map = f=> xs=> xs.map(x=> f(x));
const add = x=> y=> y + x;
const sum = reduce (add) (0);

var source = [1, 2, 3, 4, 6, 7, 8];
comp (map (sum)) (chunk (3)) (source);
//=> [ 6, 17, 8 ]

正如您所看到的,我们首先将source转换为3的块,然后我们mapsum函数转换为每个块。

当你听到人们谈论“声明性”代码时,最后一行很清楚,并且对实现几乎不担心。我们没有告诉计算机 如何完成它的工作。没有for / while循环,没有无关的变量或迭代器,没有逻辑等。

“idgaf如何,只需将source分成3组,然后对每个部分求和”

// very declaration, wow
comp (map (sum)) (chunk (3)) (source);
  

或者制作两个元素:

var source = [1, 2, 3, 4, 5, 6, 7] // to [[1, 2], [3, 4], [5, 6], [7]]

使用上面相同的代码

var source = [1, 2, 3, 4, 5, 6, 7];
chunk (2) (source);
// => [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7 ] ]
  

对于第二个我有以下但是看起来不是很有用,因为我正在通过索引访问数组:

function* pairMap(data) {
      yield* data.map((item, index) => {
         if (index > 0) {
              return [data[index - 1], item];
          }
      });
  }

使用上面的代码,您可以轻松实现pairMap

const pairMap = f=> comp (map (f)) (chunk (2));

var source = [1, 2, 3, 4, 5, 6, 7];
pairMap (pair => console.log(pair)) (source);
// [ 1, 2 ]
// [ 3, 4 ]
// [ 5, 6 ]
// [ 7 ]

了解所有内容

问题是“自定义迭代的功能方式”。您会发现我的代码使用Array.prototype.reduceArray.prototype.map排序作弊。学习如何自己构建这些是一个很好的学习工具,让我理解构建功能循环/迭代器/控件是有趣和简单的

const isEmpty = xs=> xs.length === 0
const head = xs=> xs[0];
const tail = xs=> xs.slice(1);

const reduce = f=> y=> xs=>
  isEmpty (xs)
    ? y
    : reduce (f) (f (y) (head (xs))) (tail (xs));

const add = x=> y=> y + x;
reduce (add) (0) ([1,2,3]);
//=> 6

有效!。

好的,让我们看看我们如何做地图

const concat = xs=> ys=> ys.concat(xs);
const append = x=> concat ([x]);

const map = f=>
  reduce (ys=> x=> append (f (x)) (ys)) ([]);

const sq = x => x * x;
map (sq) ([1,2,3])
//=> [ 1, 4, 9 ]

测验1:您能否使用filter撰写someeveryreduce

巨魔警告:实现这些功能的方法有很多种。如果你开始编写递归函数,你首先想要了解的是tail call是什么。 ES6正在进行尾部呼叫优化,但它暂时不会普及。有一段时间,Babel可以使用while循环来转换它,但它在版本6中暂时禁用,并且一旦修复它就会回来。

测验2 :如何通过适当的尾调用来重写reduce

答案 1 :(得分:2)

对于第一部分问题,您可以使用reduceslice对数组中的每3个元素进行分组,然后您可以使用mapreduce来获取每个元素的总和基。

var source = [1, 2, 3, 4, 6, 7, 8];

var result = source.reduce((r, elem, i) => {
  if(i % 3 == 0) r.push(source.slice(i, i+3));
  return r;
}, []);

result = result.map(e => {return e.reduce((a, el) => { return a + el })});
console.log(result)

对于问题的第二部分,您可以再次使用reduceslice对每两个元素进行分组。

var source = [1, 2, 3, 4, 5, 6, 7]

source = source.reduce((r, e, i) => {
  if(i % 2 == 0) r.push(source.slice(i, i+2));
  return r;
}, [])

console.log(source)

答案 2 :(得分:1)

第一项任务reduce



var source = [1, 2, 3, 4, 5, 6, 7];

var r1 = source.reduce((p, c, i, a) => {
    if (i % 2) p[p.length - 1].push(c);
    else p.push([a[i]]);
    return p;
}, []);

console.log(JSON.stringify(r1, 0, 2));




reducemap

的第二项任务



var source = [1, 2, 3, 4, 5, 6, 7];

var r2 = source.reduce((p, c, i, a) => {
    if (p[p.length - 1].length < 3) p[p.length - 1].push(c);
    else p.push([a[i]]);
    return p;
}, [[]]).map(e => e.reduce((a, b) => a + b));

console.log(JSON.stringify(r2, 0, 2));
&#13;
&#13;
&#13;