码迷,mamicode.com
首页 > Web开发 > 详细

[Transducer + Ramda] Write highly performance / functional code by using transducer-js and ramda.js libs

时间:2018-01-16 01:01:08      阅读:392      评论:0      收藏:0      [点我收藏+]

标签:replace   param   time   mda   man   who   last   ecif   abs   

Tansducer-js lib

A high performance Transducers implementation for JavaScript.

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.

The whole point to use transducer-js lib is because in normal FP approach, when se do:

Array
    .map
    .filter
    .reducer

We actually loop though the array 3 times. So time complexity is O(n). If you can reduce 3 times into single time. And reduce the time complexity from O(n) to O(1). It would be a huge proferemce improvement for large datasets.

Second, normal approach we are limiting ourself with Array type, we cannot call ‘mpa, filter, reduce‘ on type of Object, Map and Set. But with transducer-js, we are able to do that as well.

 

Ramda.js:

The primary distinguishing features of Ramda are:

  • Ramda emphasizes a purer functional style. Immutability and side-effect free functions are at the heart of its design philosophy. This can help you get the job done with simple, elegant code.

  • Ramda functions are automatically curried. This allows you to easily build up new functions from old ones simply by not supplying the final parameters.

  • The parameters to Ramda functions are arranged to make it convenient for currying. The data to be operated on is generally supplied last.

The last two points together make it very easy to build functions as sequences of simpler functions, each of which transforms the data and passes it along to the next. Ramda is designed to support this style of coding.

The whole point we are using Ramda.js is because it enforce the FP approach, and auto currying is a huge win in FP style. Also you can use loadsh/fp if you wish.

 

Now let‘s see what kind of problem we want to solve:

lets say we have two string:

`hostname, username `
`
    10.1.10.1, zwan
    10.1.11.1, wan

`

And we want to apply so transform function to those string and the final output should be:

[ { hostname: ‘10.1.10.1‘, username: ‘zwan‘ },
  { hostname: ‘10.1.11.1‘, username: ‘wan‘ } ]

 

Now here is one of the possible solution by only using ramda.js:

var R = require("ramda");

const headerStr = `hostname, username `;
const contentStr = `
    10.1.10.1, zwan
    10.1.11.1, wan

`;

/**
 * Working with Ramda.js
 */
const stringToArray = R.compose(
    R.map(R.trim),
    R.filter(Boolean),
    R.split(‘,‘)
);
const transformContent = R.compose(
    R.map(stringToArray),
    R.filter(Boolean),
    R.split(‘\n‘)
);
const keyValPair = R.useWith(
    R.map,
    [
        R.zipObj,
        R.identity
    ]
);

const header = stringToArray(headerStr);
const content = transformContent(contentStr);

const res = keyValPair(header, content);
console.log(res);

The solution is fine, it should be really easy to be tested and maintained. We also able to compose the logic such as :

const stringToArray = R.compose(
    R.map(R.trim),
    R.filter(Boolean),
    R.split(‘,‘)
);
const transformContent = R.compose(
    R.map(stringToArray),
    R.filter(Boolean),
    R.split(‘\n‘)
);

 

The only problem (from my point of view) is the preformence. 

When we call ‘R.map, R.filter‘, it actually loop though the array twice. When we call ‘R.map(stringToArray)‘, the nested loop might increase the time complxity to O(n ^2).

Here is the transducer-js comes to help.

var t = require("transducers-js");
var R = require("ramda");

const headerStr = `hostname, username `;
const contentStr = `
    10.1.10.1, zwan
    10.1.11.1, wan

`;
/**
 * Working with transducer-js + Rmada.js
 */

const stringToArr = R.compose(
    t.comp(
        t.map(R.trim),
        t.filter(R.isNil)
    ),
    R.split(‘,‘)
);
const transformCnt = R.compose(
    t.comp(
        t.map(stringToArr),
        t.filter(R.isNil)
    ),
    R.split(‘\n‘)
);
const keyValPair = R.useWith(
    R.map,
    [
        R.zipObj,
        R.identity
    ]
);
const h = stringToArr(headerStr);
const c = transformCnt(contentStr);
const res2 = keyValPair(h, c);
console.log("res2", res);

As you can see that, we replace

// from
const stringToArray = R.compose(
    R.map(R.trim),
    R.filter(Boolean),
    R.split(‘,‘)
);

// to
const stringToArr = R.compose(
    t.comp(
        t.map(R.trim),
        t.filter(R.isNil)
    ),
    R.split(‘,‘)
);

From ‘R.map, R.filter‘, loop twice to ‘t.comp(t.map, t.filter)‘ only loop once. ‘t.comp‘ helps to compose two transform opreation together. If you think further, this is not that easy task to combine filter‘s prediction function with map‘s transform function:

filter prediction:

f => (a → Boolean)

map transform:

f => (a → b)

Because the return type is not the same, so they cannot compose naturally. transducer-js helps us to make it easily.

 

[Transducer + Ramda] Write highly performance / functional code by using transducer-js and ramda.js libs

标签:replace   param   time   mda   man   who   last   ecif   abs   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!