[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 -> b), m a, m a) -> m 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]

原文地址:https://www.cnblogs.com/Answer1215/p/10409164.html

时间: 2024-08-10 01:02:50

[Transducer] Step by Step to build a simple transducer的相关文章

Build step ‘Execute shell‘ marked build as failure

shell 脚本远程调用执行如下:/usr/bin/ssh 10.1.2.3 '/bin/bash /root/ReServer.sh'报错如下:Build step 'Execute shell' marked build as failure 本地执行是ok,怎么执行都报错,后来折腾了老半天,一步一步的打输出,发现:原来是脚本在判断进程是否存在的同时,这个结束进程刚好有同样的匹配串!ps -aux | grep "idiot" | grep -v grep | grep -v '$

C# 2012 step by step 学习笔记8 CHAPTER 9 Creating Value types with enumerations and Structures

C# 2012 step by step 学习笔记8 CHAPTER 9 Creating Value types with enumerations and Structures things about 1. Declare an enumeration type. 2. Create and use an enumeration type. 3. Declare a structure type. 4. Create and use a structure type. 5. Explain

Step by Step for configuration of sending customize IDOC/自定义IDOC发送配置

<div style="width: 600px; max-width: 100%; margin-bottom:5px;"><a href="https://docs.com/gan-gavin/6042/idoc-step-by-step-for-configuration-of-sending" title="自定义IDOC配置/Step by Step for configuration of sending customize

搭建你的持续集成server - CruiseControl step by step(1)

CruiseControl是CIserver的老者,诞生已是多年,在很多方面,CruiseControlserver已经成为持续集成实践的同义词.而如今,CruiseControl已发展成为一个家族式系统,包含CruiseControl.java.CruiseControl.net.CruiseControl.ruby等适应不同语言环境的实现,其强大的插件和扩展能力也是诸多同类系统无法比你的.而在这里,我仅仅介绍该家族的本家CruiseControl.java,即CruiseControl.Cr

精通initramfs构建step by step

http://hi.baidu.com/jonathan2004/blog/item/db7bf38aad11759ea4c2721d.html 精通initramfs构建step by step (1)--hello world 2009-12-08 19:19 一.initramfs是什么 在2.6版本的linux内核中,都包含一个压缩过的cpio格式的打包文件.当内核启动时,会从这个打包文件中导出文件到内核的rootfs文件系统, 然后内核检查rootfs中是否包含有init文件,如果有则

第四周:Deep Neural Networks(深层神经网络)----------2.Programming Assignments: Building your Deep Neural Network: Step by Step

Building your Deep Neural Network: Step by Step Welcome to your third programming exercise of the deep learning specialization. You will implement all the building blocks of a neural network and use these building blocks in the next assignment to bui

Step by Step Guide: How to Configure SSL/TLS on ORACLE RAC (with SCAN) (Doc ID 1448841.1)

In this Document   Goal   Solution   References Applies to: Advanced Networking Option - Version 11.2.0.2 to 12.2.0.1 [Release 11.2 to 12.2]Oracle Net Services - Version 12.2.1.2.0 to 12.2.1.2.0 [Release 12.2]Information in this document applies to a

数论之高次同余方程(Baby Step Giant Step + 拓展BSGS)

什么叫高次同余方程?说白了就是解决这样一个问题: A^x=B(mod C),求最小的x值. baby step giant step算法 题目条件:C是素数(事实上,A与C互质就可以.为什么?在BSGS算法中是要求a^m在%c条件下的逆元的,如果a.c不互质根本就没有逆元.) 如果x有解,那么0<=x<C,为什么? 我们可以回忆一下欧拉定理: 对于c是素数的情况,φ(c)=c-1 那么既然我们知道a^0=1,a^φ(c)=1(在%c的条件下).那么0~φ(c)必定是一个循环节(不一定是最小的)

Git Step by Step – (8) Git的merge和rebase

前面一篇文章中提到了"git pull"等价于"git fetch"加上"git merge",然后还提到了pull命令支持rebase模式,这篇文章就介绍一下merge和rebase之间有什么差别. 由于我们主要是想看看merge跟rebase之间的区别,这里就是用本地仓库的分支进行演示了. merge 其实在介绍分支的那篇文章中已经介绍过了一些分支merge的内容,这里就进行一些补充和总结. 下面我们基于本地一个仓库开始介绍,当前仓库的分支情