函数式编程
本文最后更新于:2022年3月30日 上午
函数式编程
- 对数据之间的运算过程进行抽象
-   “函数”是数学中的“函数”a = sin(30),是一种映射关系
- 相同的输入一定会得到相同的输出(副作用)
函数是一等公民
- 函数可以存储在变量中
- 函数可以作为参数 (高阶函数)
- 函数可以作为返回值 (高阶函数)
高阶函数 => 抽象通用问题,屏蔽实现的细节
闭包closure
JUMP To:闭包
纯函数pf
- 不修改参数
- 不产生副作用
- 不调用不纯的方法
纯函数的好处
- 可缓存
// lodash.memoize()
function memoize(fn) {
	const cache = {};
	return function () {
		const key = JSON.stringify(arguments);
		cache[key] = cache[key] || fn.apply(fn, arguments);
		return cache[key];
	};
}- 可测试
-   并行处理(webWorker)
副作用effect
- 函数的状态依赖外部的状态就会带来副作用
- 副作用会使函数不纯
柯里化curry
JUMP To:柯里化
- 缓存函数的参数
- 将多元函数转化为多个一元函数
- 降低函数的粒度,方便组合
// lodash.curry()
function curry(func) {
	return function curried(...args) {
		if (args.length < func.length) {
			return function () {
				return curried(...args.concat([...arguments]));
			};
		}
		return func(...args);
	};
}函数组合compose
-   封装洋葱代码_.toUpper(._first(._reverse(array)))
- 忽略中间状态 (数据管道)
fn: A -> B -> C -> D
fn: A -----------> D- 默认从右向左执行
// lodash.flowRight()
function flowRight(...fns) {
	return function (...args) {
		return fns.reduceRight((acc, fn) => {
			return fn(acc);
		}, ...args);
	};
}
// es6
const compose =
	(...fns) =>
	(...args) =>
		fns.reduceRight((acc, fn) => fn(acc), ...args);lodash/fp 模块
- 柯里化的 lodash 方法
- 函数参数在前,数据在后
(FP Guide)[https://github.com/lodash/lodash/wiki/FP-Guide]
_.map(["1", "2", "3"], parseInt);
// [1,NaN,NaN]
// mapFn(item,index|key,array)
// parseInt(string,radix)// 将radix进制的string转为十进制
fp.map(parseInt, ["1", "2", "3"]);
// [1,2,3]PointFree
- 不需要指明处理的数据
- 只需要合成运算的过程
// Hello World => hello_world
const f = fp.flowRight(fp.replace(/\s+/g, "_"), fp.toLower);
f("Hello World");函子Functor
- 一个包裹值的盒子
- 不直接对外暴露值
- 实现 map 契约
Either函子
// Either函子
class Left {
	static of(value) {
		return new Left(value);
	}
	constructor(value) {
		this._value = value;
	}
	map(fn) {
		return this;
	}
}
class Right {
	static of(value) {
		return new Right(value);
	}
	constructor(value) {
		this._value = value;
	}
	map(fn) {
		return Right.of(fn(this._value));
	}
}
function parseJSON(str) {
	try {
		return Right.of(JSON.parse(str));
	} catch (e) {
		return Left.of({ error: e.message });
	}
}IO函子
- 将不纯的函数用函子保存
- 延迟执行副作用,并且不纯的操作交由调用者处理
class IO {
	static of(value) {
		return new IO(function () {
			return value;
		});
	}
	constructor(fn) {
		this._value = fn;
	}
	map(fn) {
		return new IO(fp.flowRight(fn, this._value));
	}
}
const path = IO.of(process).map((p) => p.execPath);
// 输出的path是一个包裹了副作用的纯函数
path._value();
// 直到执行._value()时才会处理副作用folktale
JUMP TO:https://folktale.origamitower.com/
Task函子
const { task } = require("folktale/concurrency/task");
const { split, find } = require("lodash/fp");
const fs = require("fs");
function readFile(filename) {
	return task((resolver) => {
		fs.readFile(filename, "utf-8", (err, data) => {
			if (err) resolver.reject(err);
			resolver.resolve(data);
		});
	});
}
const readVersion = readFile("package.json")
	.map(split("\n"))
	.map(find((x) => x.includes("version")))
	.run()
	.listen({
		onRejected: (err) => console.log(err),
		onResolved: (val) => console.log("res=>", val),
	});
console.log(readVersion);
// TaskExecution {
//   _task: Task { _computation: [Function (anonymous)] },
//   _deferred: Object [folktale:Deferred] {},
//   _links: []
// }
// res=>   "version": "1.0.0",Point函子
-   实现了of静态方法的函子
-   避免使用new来创建对象
- 上面的都属于 point 函子,将值包裹到上下文中
Monad函子
- 返回一个函子
-   可以扁平化的Point函子
-   实现了静态join``of方法
class IO {
	static of(value) {
		return new IO(function () {
			return value;
		});
	}
	constructor(fn) {
		this._value = fn;
	}
	map(fn) {
		return new IO(fp.flowRight(fn, this._value));
	}
	join() {
		return this._value();
	}
	flatMap(fn) {
		return this.map(fn).join();
	}
}
const readFile = function (filename) {
	return new IO(function () {
		return fs.readFileSync(filename, "utf-8");
	});
};
const print = function (x) {
	return new IO(function () {
		return x;
	});
};
const res = readFile("package.json").flatMap(print).join();函数式编程
      http://yoursite.com/2022/03/29/函数式编程/