自定义迭代的功能方式 [英] Functional way for custom iteration
问题描述
如何只使用 map
, reduce
或过滤器
或任何函数的方式来创建一个数组的自定义迭代?
假设我想映射一个数组到另一个数组中,每个数组包含三个相邻元素的总和源数组:
var source = [1,2,3,4,6,7,8] // to [ 6,17,8]
或者制作两个元素桶:
var source = [1,2,3,4,5,6,7] //到[[1,2],[3,4] ,[5,6],[7]]
对于第二个,我有以下但看起来没有什么功能,因为我按索引访问数组:
function * pairMap(data){
如果(索引> 0){
return [data [index-1],item];
} $ b,则返回* data.map((item,index)=> $ b});
}
我对这样做的功能感兴趣。
假设我想将一个数组映射到另一个数组,源数组中的元素:
var source = [1,2,3,4,6,7,8] //至[6,17,8]
地图创建1:1关系,所以这不适合使用 map
。相反, reduce
或(fold)在这里会更好。
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 => ((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块,然后我们在每个块上 map
sum
函数。
当您听到有人在谈论声明式代码时,最后一行非常清晰,并且对实现操作很少担心。我们并没有告诉计算机如何做它的工作。没有用于
/ ,而
循环,没有额外的变量或迭代器,没有逻辑等。
$ b
idgaf怎么样,只要把 source
分成3个组,然后求和每个部分
//非常声明,哇
comp(map(sum))(chunk(3) )) (资源);
桶中的两个元素:
var source = [1,2,3,4,5,6,7] //到[b] [b]
$ [[1,2],[3,4],[5,6],[7] b
$ b
使用上面相同的代码
var source = [1,2,3,4 ,5,6,7];
,
chunk(2)(source);
// => [b] [b] [b] [b] [b] [b] [b] [b] [b] [b] [b] [b] [b] hr>
对于第二个,我有以下内容,但由于我按索引访问数组,因此看起来不太有用:
函数* pairMap(data){
yield * data.map((item,index)=> {
if (index> 0){
return [data [index - 1],item];
}
});
$ / code>
使用上面的代码,您可以实现
pairMap
easy
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.reduce
和Array.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(f)(f(y)(head(xs)))(tail(xs));
const add = x => Y => y + x;
reduce(add)(0)([1,2,3]);
// => 6
确实有效!
确定,让我们看看我们该怎么做map
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: code> filter
一些
和每个
使用减少
?
巨魔警告:实现这些功能的方法有很多。如果您开始编写递归函数,您首先需要了解的是 tail call
是。 ES6正在实现尾部呼叫优化,但一段时间后它不会普及。有一段时间,Babel可以使用while循环来传递它,但它在版本6中暂时被禁用,并且一旦修复就会回来。
测验2:如何用适当的尾声重写我的
reduce
?How can I use only
map
,reduce
orfilter
or any functional way to create a custom iteration on an array?Let's say I wanna map an array to another array which holds the sum of each three adjacent elements in the source array:
var source = [1, 2, 3, 4, 6, 7, 8] // to [6, 17, 8]
Or make a bucket of two elements:
var source = [1, 2, 3, 4, 5, 6, 7] // to [[1, 2], [3, 4], [5, 6], [7]]
For the second one I have the following but that doesn't look very functional as I'm accessing array by index:
function* pairMap(data) { yield* data.map((item, index) => { if (index > 0) { return [data[index - 1], item]; } }); }
I'm interested in the functional way of doing this.
解决方案Let's say I wanna map an array to another array which holds the sum of each three adjacent elements in the source array:
var source = [1, 2, 3, 4, 6, 7, 8] // to [6, 17, 8]
Maps create 1:1 relationships, so this wouldn't be appropriate use of
map
. Instead, areduce
or ("fold") would be better here.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 ]
So as you can see, we first transform the
source
into chunks of 3, then wemap
thesum
function over each chunk.When you hear people talking about "declarative" code, the last line is quite clear and worries little about implementation. We're not telling the computer how to do its job. There's no
for
/while
loops, no extraneous vars or iterators, no logic, etc."idgaf how, just break
source
up into groups of 3, and then sum each part"// very declaration, wow comp (map (sum)) (chunk (3)) (source);
Or make a bucket of two elements:
var source = [1, 2, 3, 4, 5, 6, 7] // to [[1, 2], [3, 4], [5, 6], [7]]
Using the same code above
var source = [1, 2, 3, 4, 5, 6, 7]; chunk (2) (source); // => [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7 ] ]
For the second one I have the following but that doesn't look very functional as I'm accessing array by index:
function* pairMap(data) { yield* data.map((item, index) => { if (index > 0) { return [data[index - 1], item]; } }); }
Using the code above, you can implement
pairMap
easilyconst 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 ]
Learn all the things
The question is "functional way for custom iteration". You'll notice my code sorta cheats by using
Array.prototype.reduce
andArray.prototype.map
. Learning how to build these on your own was a good learning tool for me to understand that building functional loops/iterators/control is fun and simpleconst 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
It works!.
OK, let's see how we'd do map
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 ]
Quiz 1: Can you write
filter
,some
, andevery
usingreduce
?Troll warning: There's many different ways to implement these functions. If you start writing recursive functions, the first thing you'll want to learn about is what a tail call is. ES6 is getting tail call optimization but it won't be widespread for a while. For a while, Babel could transpile it using a while loop, but it's temporarily disabled in version 6 and will be coming back once they fix it.
Quiz 2: How can you rewrite my
reduce
with a proper tail call?这篇关于自定义迭代的功能方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!