函数式编程

本文最后更新于: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/函数式编程/
作者
tatekii
发布于
2022年3月29日
许可协议