msforest / notebook

好记性不如烂笔头,记录知识的点点滴滴。
https://github.com/msforest/notebook/wiki
0 stars 0 forks source link

transducer in javascript #32

Open msforest opened 5 years ago

msforest commented 5 years ago

了解transducer,首先要了解compose/pipe知识点。

compose / pipe

见名知义,两者都是对上一次的操作结果进行处理。区别是处理的方向是相反的,compose 是从右向左依次执行,pipe是从左向右依次执行。

var pipe = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
var compose = (...fns) => fns.reduceRight((f, g) => (...args) => g(f(...args)));

// 便于更好区分
var pipe = (...fns) => fns.reduceRight((f, g) => (...args) => f(g(...args)));
var compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));

让我们实现一个函数对字符串进行各种变换

var getName = (person) => person.name
getName({ name: 'Buckethead' })  // 'Buckethead'

var uppercase = (string) => string.toUpperCase()
uppercase('Buckethead')  // 'BUCKETHEAD'

// 现在组合以上两个方法,可以这么实现
var name = getName({ name: 'Buckethead' })
uppercase(name)  // 'BUCKETHEAD'
// or
uppercase(getName({ name: 'Buckethead' }))

// 如果还要实现字符串截取
var get6Characters = (string) => string.substring(0, 6)
get6Characters('Buckethead')  // 'Bucket'

// 最终可能会写成这样
get6Characters(uppercase(getName({ name: 'Buckethead' }))) //'BUCKET'

以上代码看起来不怎么舒服

我们可以借助pipe/compose方法来编写让人赏心悦目的代码

pipe(
  getName,
  uppercase,
  get6Characters,
  reverse 
)({ name: 'Buckethead' })
// 'TEKCUB'

参考

msforest commented 5 years ago

问题

var arr = [
    {
        id: 'id',
        name: 'name',
        ...
    },
    {
        id: 'id2',
        name: 'name2',
        ...
    },
    ...
]
var arr1 = arr.filter((item) => /name/i.test(item.name));
var arr2 = arr.map((item) => (item.compute = 'compute', item));
...

很多时候,filter/map等高阶函数经常会混合在一起使用,当数组数据量越来越大的情况下,会不得不考虑性能,就会使用另外一种不是太友好的写法。

var arr2 = [];
// 使用 for / while / reduce 也有可能
arr.forEach((item) => {
    if (!/name/i.test(item.name)) { // logic1
        return;
    }

    item.compute = 'compute';   // logic2
    arr2.push(item);

    ... // logicN
});

这种写法就使得业务不清晰、不明确;当N越大的时候,会不知道这个循环到底是何用意。

思考

...(随着知识的积累,不知道过了多久)

从middleware那里了解到compose,从mongodb那里了解到aggregate,从linux那里了解到管道符等这些概念,都是很类似的效果,像水流一样,从源头按顺序经过处理流向下游,这和reduce有点相像,reduce都是经过相同的处理功能。 image

结局

然后就有了大家公认的transducer概念:transducer = transform(==compose) + reduce。实现如下

function transducer(transformer, reducer, initial, array) {
    return array.reduce(transformer(reducer), initial);
}

// 等同于
const transduce = (transformer, reducer, initial, array) => array.reduce(transformer(reducer), initial);

参考

msforest commented 4 years ago

What is {} + {} in JavaScript?