一等函数与Applicative编程
一等函数与Applicative编程
函数是一等公民
// 函数与数字一样可以存储为变量
let foo = function(params) {
doSomething();
}
// 函数与数字一样可以存储为数组的一个元素
let arr = [ 0, 1, foo ];
// 函数与数字一样可以作为对象的成员变量
let obj = {
name: "test",
age: 19,
foo
};
// 函数与数字一样可以在使用时直接创建出来
let res = 1 + (function() { return 2; })(); // 3
// 函数与数字一样可以被传递给另一个函数
function weirdAdd(n, f) { return n + f(); }
weirdAdd(123, () => 110); // 233
// 函数与数字一样可以被另一个函数返回
return 0;
return function() { return -1; };
/*
高阶函数
以一个函数作为参数
返回一个函数作为结果
*/
多种 JavaScript 编程方式
命令式编程
通过详细描述行为的编程方式.
基于原型的面向对象编程
基于原型对象及其实例的编程方式
元编程
对 JavaScript 执行模型数据进行编写和操作的编程方式
let { _ } = require("underscore");
// 命令式编程
let lyrics = [];
for (let bottles = 99; bottles > 0; bottles--) {
lyrics.push(`${bottles} bottles of beer on the wall`);
lyrics.push(`${bottles} bottles of beer`);
lyrics.push("Take one down, pass it around");
if (bottles > 1) {
lyrics.push(`${bottles - 1} bottles of beer on the wall.`);
} else {
lyrics.push(`No more bottles of beer on the wall!`);
}
}
// 函数式编程
function lyricSegment(n) {
return _.chain([])
.push(`${n} bottles of beer on the wall`)
.push(`${n} bottles of beer`)
.push(`Take one down, pass it around`)
.tap((lyrics) => {
if (n > 1) {
lyrics.push(`${n - 1} bottles of beer on the wall.`);
} else {
lyrics.push(`No more bottles of beer on the wall!`);
}
})
.value();
}
const range = (start, stop, step) =>
Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);
function song(start, end, lyricGen) {
/* return _.reduce(
_.range(start, end, -1),
(acc, n) => acc.concat(lyricGen(n)),
[]
); */
return range(start, end, -1).reduce(
(pre, cur) => pre.concat(lyricGen(cur)),
[]
);
}
song(9, 0, lyricSegment);
// 基于原型的面向对象编程
let a = {
name: "a",
fun() {
return this;
},
};
a.fun(); // a{}
let bFunc = function () {
return this;
};
let b = {
name: "b",
bFunc,
};
b.bFunc(); // b{}
// 元编程
/* function Point2D(x, y) {
this._x = x;
this._ = y;
} */
class Point2D {
constructor(x, y) {
this._x = x;
this._ = y;
}
}
new Point2D(0, 1); // Point2D { _x: 0, _: 1 }
/* function Point3D(x, y, z) {
Point2D.call(this, x, y);
this._y = y;
} */
class Point3D extends Point2D {
constructor(x, y, z) {
super(x, y);
this._z = z;
}
}
new Point3D(1, 0, 1); // Point3D { _x: 1, _: 0, _z: 1 }
Applicative 编程
Applicative 编程定义为函数A作为参数提供给函数B.
let nums = [1, 2, 3, 4, 5];
function doubleAll(array) {
// return _.map(array, (n) => n * 2);
return array.map((n) => n * 2);
}
doubleAll(nums); // [ 2, 4, 6, 8, 10 ]
function average(array) {
/* let sum = _.reduce(array, (a, b) => a + b);
return sum / _.size(array); */
let sum = array.reduce((pre, cur) => pre + cur);
return sum / array.length;
}
average(nums); // 3
function onlyEven(array) {
// return _.filter(array, (n) => !Boolean(n % 2));
return array.filter((n) => !Boolean(n % 2));
}
onlyEven(nums); // [ 2, 4 ]
集合中心编程
_.map({ a: 1, b: 2 }, _.identity); // [ 1, 2 ]
console.log(res);
_.map({ a: 1, b: 2 }, (v, k) => [k, v]); // [ [ 'a', 1 ], [ 'b', 2 ] ]
console.log(res);
// _.keys => Object.keys
_.map({ a: 1, b: 2 }, (v, k, coll) => [k, v, _.keys(coll)]); // [ [ 'a', 1, [ 'a', 'b' ] ], [ 'b', 2, [ 'a', 'b' ] ] ]
Applicative 编程的其他实例
// 1.reduceRight
let nums = [100, 2, 25];
function divi(x, y) {
return x / y;
}
_.reduce(nums, divi);
nums.reduce(divi); // 2
_.reduceRight(nums, divi); // 0.125
function allOf() {
return _.reduceRight(arguments, (truth, f) => truth && f(), true);
}
function anyOf() {
return _.reduceRight(arguments, (truth, f) => truth || f(), false);
}
function T() {
return true;
}
function F() {
return false;
}
allOf(); // true
allOf(T, T); // true
allOf(T, F, T); // false
anyOf(); // false
anyOf(F, F); // false
anyOf(F, T, F); // true
// 2.find
_.find(["a", "b", 3, 4, "d"], _.isNumber); // 3
// 3.reject
_.reject(["a", "b", 3, "d"], _.isNumber); // [ 'a', 'b', 'd' ]
function complement(pred) {
return function () {
return !pred.apply(null, _.toArray(arguments));
};
}
_.filter(["a", "b", 3, "d"], complement(_.isNumber)); // [ 'a', 'b', 'd' ]
// 4.all
_.all([1, 2, 3, 4], _.isNumber); // true
// 4.any
_.any([1, 2, "c", 4], _.isString); // true
// 6.sortBy, groupBy, countBy
let people = [
{ name: "Rick", age: 30 },
{ name: "Jake", age: 24 },
];
_.sortBy(people, (p) => p.age); // [ { name: 'Jake', age: 24 }, { name: 'Rick', age: 30 } ]
let albums = [
{ title: "Sabbath Bloody Sabbath", genre: "Metal" },
{ title: "Scientist", genre: "Dub" },
{ title: "Undertow", genre: "Metal" },
];
_.groupBy(albums, (a) => a.genre);
/*
{
Metal: [
{ title: 'Sabbath Bloody Sabbath', genre: 'Metal' },
{ title: 'Undertow', genre: 'Metal' }
],
Dub: [ { title: 'Scientist', genre: 'Dub' } ]
}
*/
_.countBy(albums, (a) => a.genre); // { Metal: 2, Dub: 1 }
定义几个Applicative函数
function cat() {
let head = _.first(arguments);
if (existy(head)) {
return head.concat.apply(head, _.rest(arguments));
} else {
return [];
}
}
function construct(head, tail) {
return cat([head], _.toArray(tail));
}
// Applicative函数
function mapcat(fun, coll) {
return cat.apply(null, _.map(coll, fun));
}
mapcat((e) => construct(e, [","]), [1, 2, 3]); // [ 1, ',', 2, ',', 3, ',' ]
function butlast(coll) {
return _.toArray(coll).slice(0, -1);
}
function interpose(inter, coll) {
return butlast(mapcat((e) => construct(e, [inter]), coll));
}
interpose(",", [1, 2, 3]); // [ 1, ',', 2, ',', 3 ]
数据思考
let zombie = {
name: "Bub",
film: "Day of the Dead",
};
_.keys(zombie); // [ 'name', 'film' ]
_.values(zombie); // [ 'Bub', 'Day of the Dead' ]
let books = [
{
title: "Chthon",
author: "Anthony",
},
{
title: "Grendel",
author: "Gardner",
},
{
title: "After Dark",
},
];
_.pluck(books, "author"); // [ 'Anthony', 'Gardner', undefined ]
_.pairs(zombie); // [ [ 'name', 'Bub' ], [ 'film', 'Day of the Dead' ] ]
_.object(_.map(_.pairs(zombie), (pair) => [pair[0].toUpperCase(), pair[1]])); // { NAME: 'Bub', FILM: 'Day of the Dead' }
_.invert(zombie); // { Bub: 'name', 'Day of the Dead': 'film' }
_.keys(_.invert({ a: 123, b: 110 })); // [ '110', '123' ]
_.pluck(
_.map(books, (obj) => _.defaults(obj, { author: "Unknow" }), "author")
); // [ "Anthony", "Gardner", "Unknow" ]
let person = {
name: "Romy",
token: "j3983ij",
password: "tigress",
};
let info = _.omit(person, "token", "password"); // { name: 'Romy' }
let creds = _.pick(person, "token", "password"); // { token: 'j3983ij', password: 'tigress' }
let library = [
{ title: "SICP", isbn: "0262010771", ed: 1 },
{ title: "SICP", isbn: "0262510871", ed: 2 },
{ title: "Joy of Clojure", isbn: "1935182641", ed: 1 },
];
_.findWhere(library, { title: "SICP", ed: 2 }); // { title: 'SICP', isbn: '0262510871', ed: 2 }
_.where(library, { title: "SICP" });
/*
[
{ title: 'SICP', isbn: '0262010771', ed: 1 },
{ title: 'SICP', isbn: '0262510871', ed: 2 }
]
*/
_.pluck(library, "title"); // [ 'SICP', 'SICP', 'Joy of Clojure' ]
function project(table, keys) {
return _.map(table, (obj) => _.pick.apply(null, construct(obj, keys)));
}
let editionResults = project(library, ["title", "isbn"]);
/*
[
{ title: 'SICP', isbn: '0262010771' },
{ title: 'SICP', isbn: '0262510871' },
{ title: 'Joy of Clojure', isbn: '1935182641' }
]
*/
let isbnResult = project(editionResults, ["isbn"]);
/*
[
{ isbn: '0262010771' },
{ isbn: '0262510871' },
{ isbn: '1935182641' }
]
*/
_.pluck(isbnResult, "isbn"); // [ '0262010771', '0262510871', '1935182641' ]
function rename(obj, newNames) {
return _.reduce(
newNames,
(o, nu, old) => {
if (_.has(obj, old)) {
o[nu] = obj[old];
return o;
} else {
return o;
}
},
_.omit.apply(null, construct(obj, _.keys(newNames)))
);
}
rename({ a: 1, b: 2 }, { a: "AAA" }); // { b: 2, AAA: 1 }
function as(table, newNames) {
return _.map(table, (obj) => rename(obj, newNames));
}
as(library, { ed: "edition" });
/*
[
{ title: 'SICP', isbn: '0262010771', edition: 1 },
{ title: 'SICP', isbn: '0262510871', edition: 2 },
{ title: 'Joy of Clojure', isbn: '1935182641', edition: 1 }
]
*/
project(as(library, { ed: "edition" }), ["edition"]); // [ { edition: 1 }, { edition: 2 }, { edition: 1 } ]
function restrict(table, pred) {
return _.reduce(
table,
(newTable, obj) => {
if (truthy(pred(obj))) {
return newTable;
} else {
return _.without(newTable, obj);
}
},
table
);
}
restrict(library, (book) => book.ed > 1); // [ { title: 'SICP', isbn: '0262510871', ed: 2 } ]
restrict(
project(as(library, { ed: "edition" }), ["title", "isbn", "edition"]),
(book) => book.edition > 1
); // [ { title: 'SICP', isbn: '0262510871', edition: 2 } ]
// SQL
/* select title, isbn, edition
from (select ed as edition from library) eds
where edition > 1; */
总结—-一等函数
- 它们可以存储在变量中.
- 它们可以 被存储在数组中的插槽中.
- 它们可以存储在对象的字段中.
- 它们可以根据需要来创建.
- 它们可以被传递到其他函数中.
- 它们可以被其他函数返回.