[Ramdajs] FP了解一下~~~

715 阅读4分钟

目录

  1. Ramda 主要特性
  2. 主要风格 pointFree curry partial
  3. 分支语句 R.ifElse R.when...
  4. 分支语句 R.cond...
  5. 函数组合 pipe compose...
  6. 遍历:map...

使用了多年的库,再次推荐给大家~

Ramda 主要特性

Ramda 主要特性如下

  • Ramda 强调更加纯粹的函数式风格。数据不变性和函数无副作用是其核心设计理念。这可以帮助你使用简洁、优雅的代码来完成工作。
  • Ramda 函数本身都是自动柯里化的。这可以让你在只提供部分参数的情况下,轻松地在已有函数的基础上创建新函数。
  • Ramda 函数参数的排列顺序更便于柯里化。要操作的数据通常在最后面

最后两点一起,使得将多个函数构建为简单的函数序列变得非常容易,每个函数对数据进行变换并将结果传递给下一个函数。Ramda 的设计能很好地支持这种风格的编程。

主要风格

  1. pointFree 无参数风格
  2. 柯里化 所有方法都支持柯里化
  3. 偏函数 partial

1) pointFree 无参数风格

Ramda 先接受函数参数,最后接受数据参数 柯里化和 “函数优先” 这两者相结合,使开发者在最终传入数据之前,能够以非常少的代码(通常为 “point-free” 风格,也即无参数风格)来组合函数。

例如

// Underscore/Lodash style:
var validUsersNamedBuzz = function(users) { // 参数
  return _.filter(users, function(user) { // 参数
    return user.name === 'Buzz' && _.isEmpty(user.errors); // 参数
  });
};
// Ramda style:
var validUsersNamedBuzz = R.filter(R.where({name: 'Buzz', errors: R.isEmpty}));

2) 柯里化 所有方法都支持柯里化

也就是说,所有多参数的函数,默认都可以单参数使用。

自动柯里化使得 “通过组合函数来创建新函数” 变得非常容易。因为 API 都是函数优先、数据最后(先传函数,最后传数据参数),你可以不断地组合函数,直到创建出需要的新函数,然后将数据传入其中

关于柯里化的实现可以 参考 我的另一篇文章 高频面试题】函数柯里化的实现

function curry(fn){
	if(fn.length <=1){return fn}
	var returnFn = (...args) => {
		//console.log(...args,typeof ...args);
		if(fn.length === args.length){
			//参数都传齐时
			return fn(...args)
		}else{
			//参数没传齐时,就返回一个函数
			return (...args2) => {
				console.log(args2,"     ",args);
				return returnFn(...args,...args2)
			}
			
		}
	}
	return returnFn
}
var add = (a,b,c,d) => a+b+c+d;
//包装add
var returnFn = curry(add);
// 递归传递参数给returnFn
var returnFnArrowFn = returnFn(1)(2)(3);
// 参数传齐,returnFn将参数传递给输入函数fn, 并调用fn
returnFnArrowFn(4);// 10

3) 偏函数 partial

允许多参数的函数接受一个数组,指定最左边的部分参数

var multiply2 = (a, b) => a * b;
var double = R.partial(multiply2, [2]);
double(2) // 4

var fn = (a, b, c, d) =>
  a + b + c +  d

var one = R.partial(fn, ['a']);
var all = R.partial(one, ['b']);
all('c', 'd');
'abcd'

三 分支语句 R.ifElse R.when

const incCount = R.ifElse(
    R.has('count'),
    R.over(R.lensProp('count'), R.inc),
    R.assoc('count', 1)
  );
  incCount({});           //=> { count: 1 }
  incCount({ count: 1 }); //=> { count: 2 }
// truncate :: String -> String
const truncate = R.when(
    R.propSatisfies(R.gt(R.__, 10), 'length'),
    R.pipe(R.take(10), R.append('…'), R.join(''))
  );
  truncate('12345');         //=> '12345'
  truncate('0123456789ABC'); //=> '0123456789…'

四 分支语句 R.cond

一般用来代替 swith case 或者 代替 if...else if ...else if ...else...

const fn = R.cond([
    [R.equals(0),   R.always('water freezes at 0°C')],
    [R.equals(100), R.always('water boils at 100°C')],
    [R.T,           temp => 'nothing special happens at ' + temp + '°C']
  ]);
  fn(0); //=> 'water freezes at 0°C'
  fn(50); //=> 'nothing special happens at 50°C'
  fn(100); //=> 'water boils at 100°C'

五 函数组合 pipe compose...

1) pipe compose的使用

pipe

pipe 函数的结果不是自动柯里化的

const f = R.pipe(Math.pow, R.negate, R.inc);

f(3, 4); // -(3^4) + 1 // -80

compose

// 3先传给inc,inc执行结果作为参数传给negate.
const f = R.compose(R.negate, R.inc); // 加1后取反
f(3);  // -4

compose 输出的函数不会自动进行柯里化。

2) pipe compose的实现

参考我的另一篇文章 : 【高频面试题】compose和pipe的实现

六 遍历:map...

Ramdajs中的 mapjs原生 map 最大区别:是可以处理数组对象,而原生的只可以处理数组.还有就是R.map是柯里化的

var double = x => x * 2;
R.map(double)([1, 2, 3]) 
// [2, 4, 6]

var double = x => x * 2;
R.map(double)({x: 1, y: 2, z: 3})
// {x: 2, y: 4, z: 6}

参考

总结

  • 除了数据放在最后一个参数,Ramda 还有一个特点:所有方法都支持柯里化, 也就是说,所有多参数的函数,默认都可以单参数使用
  • Ramdajs中的 mapjs原生 map 最大区别:是可以处理数组对象,而原生的只可以处理数组.还有就是R.map是柯里化的
  • Ramda 强大的API很多,文章只列举一些,感兴趣的移步官网,哈哈,也可以和我多交流