• [Transducer] Step by Step to build a simple transducer


    Transducers are composable algorithmic transformations. They are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element. Because transducers are decoupled from input or output sources, they can be used in many different processes - collections, streams, channels, observables, etc. Transducers compose directly, without awareness of input or creation of intermediate aggregates.

    OK, short in description... let's see why we need it

    1. Normal Javascript `map`,`filter` create inter variable and loop though array everytime we call `map` or `filter` function, `transducer` can loop thought the array only ONCE and apply all the transofrmations necessary.
    const data = [1,2,3];
    const inc = x => x + 1;
    const double = x => 2 * x;
    const lessThanThree = x => x < 3;
    ////////////////////
    /**
     * Problem: We loop over array 3 times! We want to loop over only once
     * in order to improve the profermance.
     */
    const res1 = data
        .filter(lessThanThree)
        .map(double)
        .map(inc)
    
    console.log(res1)    // [3,5]

      2. We don't want to introduce any mutation or impure function such as `forEach` does, transducer are mutation free.

    /**
     * Problem: it is not pure function and we do mutation. But it is faster
     * than we do .filter.map.map style, because it only loop the array once.
     */
    let res2 = [];
    data.forEach((x) => {
        let item;
        if (lessThanThree(x)) {
            item = inc(double(x))
            res2.push(item);
        }
    })
    console.log(res2)    // [3,5]

      3. We want to style functional style to keep the code more readable, in the meanwhile improve the proferemance:

    /**
     * Good: We avoid the mutation and can be write as pure function and it only loop once!
     * Problem: But we lose our function composion style! We still want .filter.map.map styling.
     * Meanwhile it should be profermance wise.
     */
    const res3 = data.reduce((acc, curr) => {
        if (lessThanThree(curr)) {
            acc.push(inc(double(curr)));
        }
        return acc;
    }, []);
    console.log(res3);    // [3,5]

    OK, until now, we have some idea, what kind of code we want. Basiclly it should be composable and efficient.

    The question is how to make composable code?

    As we might know about, in OOP; if we want to chain multi function calls, from each function, we need to return `this`:

    Class Bot {
        ...
        sayName() {
            console.log(this,name)
            return this;
         }
    
        sayHello() {
            console.log("Hello")
            return this;
        }
    
    }
    
    const b = new Bot('Petter')
    
    b.sayName().sayHello() 

    For Array, the reason we can chain calls together is because each call return Array type. The same as String, number...

    The key is we need to keep the input and output as the same type!

    Therefore for function, we need to keep input function and output function have the same function signature! 

    //data.reduce(reducer, seed), reducer is something we can compose!
    //Because reducer :: (acc, curr) => acc
    //For every reducer functions' signature are the same.
    //If the function sinature are the same, then we can compose function together!
    const _mapReducer = (xf, array) => 
        array.reduce((acc, curr) => {
            acc.push(xf(curr))
            return acc;
        }, []);
    const _filterReducer = (xf, array) => 
        array.reduce((acc, curr) => {
            if (xf(curr)) acc.push(curr);
            return acc;
        }, []);
    // To make fns easy to compose, we extract 'array' data & init value
    const mapReducer = (xf) => ((acc, curr) => {
        acc.push(xf(curr))
        return acc;
    });
    const filterReducer = pred => ((acc, curr) => {
        if (pred(curr)) acc.push(curr);
        return acc;
    });
    // now mapReducer and filterReducer both have the same function signature.
    console.log(data.reduce(mapReducer(double), [])); // [2,4,6]
    console.log(data.reduce(mapReducer(inc), [])); // [2,3,4]
    console.log(data.reduce(filterReducer(lessThanThree), []));  // [1,2]
    In order to compose reudcers together we need to make mapReducer and filterReducer as high order functions to take reducer as arguement, take a reducer as input and return a reducer signature as output is the key to do composion!
    // In order to compose reudcers together we need to make mapReducer and filterReducer as high order functions to take reducer as arguement
    // Take a reducer as input and return a reducer signature as output is the key to do composion!
    const map = xf => reducer => ((acc, curr) => {
        acc = reducer(acc, xf(curr))
        return acc;
    });
    const filter = pred => reducer => ((acc, curr)=> {
        if (pred(curr)) acc = reducer(acc, curr) 
        return acc;
    })
    // For mapReducer and filterReducer, we both do acc.push()
    // therefore we can extrat this as base reducer
    const pushReducer = (acc, value) => {
        acc.push(value);
        return acc;
    };
    Now we are able to use functional style and loop the array only once!
    const doulbeLessThanThree = compose(
        map(inc),
        map(double),
        filter(lessThanThree)
    )
    const res5 = data.reduce(doulbeLessThanThree(pushReducer),  []);
    console.log(res5); // [3,5]

    Define our transducer!

    /**
     * transducer :: ((a -> b -> a), (a -> b -> a), [a], [a]) -> [a]
     * @param {*} xf: base reducer 
     * @param {*} reducer: the composion redcuer signature
     * @param {*} seed : init value
     * @param {*} collection : data
     */
    const transducer = (xf, reducer, seed, collection) => {
        return collection.reduce(reducer(xf), seed);
    }
    const res6 = transducer(pushReducer, doulbeLessThanThree, [], data);
    console.log(res6); // [3,5]
  • 相关阅读:
    mysql5.7.10 源码编译安装记录 (centos6.4)【转】
    bash脚本里su命令执行
    linux服务器登录时慢出现卡顿
    iptables NAT规则【转】
    双机/RAC/Dataguard的区别【转】
    一步一步搭建 oracle 11gR2 rac + dg 之前传 (一)【转】
    一步一步搭建oracle 11gR2 rac+dg之环境准备(二)【转】
    一步一步搭建oracle 11gR2 rac+dg之共享磁盘设置(三)【转】
    一步一步搭建 oracle 11gR2 rac+dg之grid安装(四)【转】
    一步一步搭建oracle 11gR2 rac+dg之database安装(五)【转】
  • 原文地址:https://www.cnblogs.com/Answer1215/p/10409164.html
Copyright © 2020-2023  润新知