Notes
1、高阶函数的概念:Functions that operate on other functions, either by taking them as arguments or by returning them, are called higher-order functions.
2、自己yy的高阶函数优势:
- 更少的代码。有效减少键盘磨损
- 传统编程是复用对象、方法,高阶函数则是复用一种更加抽象的模式
- 对于理解、熟练运用高阶函数的人而言,采用高阶函数比传统代码有更好的可读性
高阶函数是函数式编程的一种特性,当然js并不是函数编程语言,对于函数式编程可以参考—— 什么是函数式编程思维? - 用心阁的回答
3、代码收集:
① 高阶函数示例1
function greaterThan(n) { return m => m > n; } let greaterThan10 = greaterThan(10); console.log(greaterThan10(11)); // → true
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
② 高阶函数示例2
function noisy(f) { return (...args) => { console.log("calling with", args); let result = f(...args); console.log("called with", args, ", returned", result); return result; }; } noisy(Math.min)(3, 2, 1); // → calling with [3, 2, 1] // → called with [3, 2, 1] , returned 1
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
③ 高阶函数示例3
function unless(test, then) { if (!test) then(); } repeat(3, n => { unless(n % 2 == 1, () => { console.log(n, "is even"); }); }); // → 0 is even // → 2 is even
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
["A", "B"].forEach(l => console.log(l)); // → A // → B
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
function filter(array, test) { let passed = []; for (let element of array) { if (test(element)) { passed.push(element); } } return passed; } console.log(filter(SCRIPTS, script => script.living)); // → [{name: "Adlam", …}, …]
标准数组方法:
console.log(SCRIPTS.filter(s => s.direction == "ttb")); // → [{name: "Mongolian", …}, …]
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
⑥ filter会生成过滤掉一些数据的新数组,map则从原有的数组构造一个长度相同、但数据经过转化的新数组。
function map(array, transform) { let mapped = []; for (let element of array) { mapped.push(transform(element)); } return mapped; } let rtlScripts = SCRIPTS.filter(s => s.direction == "rtl"); console.log(map(rtlScripts, s => s.name)); // → ["Adlam", "Arabic", "Imperial Aramaic", …]
map同样是一个标准的数组方法。
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
⑦ 高阶函数reduce(或者说fold):通过对数组中的元素逐个进行某种累加运算产出一个结果。例如说求和sum(array),翻译成高阶函数就是reduce(array, (a, b) => a + b, 0)
function reduce(array, combine, start) { let current = start; for (let element of array) { current = combine(current, element); } return current; } console.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0)); // → 10
标准数组方法:
console.log([1, 2, 3, 4].reduce((a, b) => a + b)); // → 10
reduce用于比较最后得出一个结果:
function characterCount(script) { return script.ranges.reduce((count, [from, to]) => { return count + (to - from); }, 0); } console.log(SCRIPTS.reduce((a, b) => { return characterCount(a) < characterCount(b) ? b : a; })); // → {name: "Han", …}
更简单的例子:
[1, 23, 21, 12, 3, 5].reduce((a, b) => a > b ? a : b); // → 23
[1, 23, 21, 12, 3, 5].reduce((a, b) => { console.log(`compare ${a} with ${b}`); return a > b ? a : b; }); // → compare 1 with 23 // → compare 23 with 21 // → compare 23 with 12 // → compare 23 with 3 // → compare 23 with 5 // → 23
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
[2, 3, 2, 1, 2, 5, 7].some(a => a > 7); // → false [2, 3, 2, 1, 2, 5, 7].some(a => a >= 7); // → true [2, 3, 2, 1, 2, 5, 7].some(a => { console.log(`start testing ${a}`); return a == 1; }); // → start testing 2 // → start testing 3 // → start testing 2 // → start testing 1 // → true
测试数组中的元素,只要其中任何一个满足就返回true
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
[2, 3, 2, 1, 2, 5, 7].findIndex(a => a > 7); // → -1 [2, 3, 2, 1, 2, 5, 7].findIndex(a => a >= 7); // → 6 [2, 3, 2, 1, 2, 5, 7].findIndex(a => { console.log(`start testing ${a}`); return a == 1; }); // → start testing 2 // → start testing 3 // → start testing 2 // → start testing 1 // → 3
数据:http://eloquentjavascript.net/code/scripts.js
数据格式:
{ name: "Adlam", ranges: [[125184, 125259], [125264, 125274], [125278, 125280]], direction: "rtl", year: 1987, living: true, link: "https://en.wikipedia.org/wiki/Fula_alphabets#Adlam_alphabet" }
主代码:
/** * 字符是由各种字符脚本处理的 * 通过每个字符的编码可以找到该字符的脚本归属 * 例如编码为1~100的字符由a脚本处理 * 编码为300~400、500~700的由b脚本处理 */ /** * 课本上的函数一般直接用“代表返回值的名词”做函数名 * 而java中这种情况一般用“get+代表返回值的名词”做函数名 * 如果觉得某个函数意义模糊,可以自行在函数前面脑补动词 */ const textToCodes = (text) => { let result = []; for (let char of text) { result.push(char.codePointAt(0)); } return result; }; const codeToScriptName = (code) => { let result = null; for (let script of SCRIPTS) { if (script.ranges.some(([from, to]) => { return code >= from && code < to; })) { result = script.name; break; } } return result; }; const elementCounts = (arr) => { let result = []; for (let element of arr) { let index = result.findIndex(a => a.label == element); if (index == -1) { result.push({label: element, count: 1}); } else { result[index].count++; } } return result; }; const scriptsRate = (text) => { let scriptNames = textToCodes(text).map(code => codeToScriptName(code)); let total = scriptNames.length; let scriptCounts = elementCounts(scriptNames); return scriptCounts.map(({label, count}) => { return { label: label == null ? "none" : label, rate: (count / total).toFixed(2) }; }); }; const main = () => { let text = '英国的狗说"woof", 俄罗斯的狗说"тяв"'; console.log(scriptsRate(text).map(item => { return `${item.rate * 100}% ${item.label}`; }).join(", ")); }; main(); // → 46% Han, 25% none, 17% Latin, 13% Cyrillic
Exercises
let arrays = [[1, 2, 3], [4, 5], [6]]; arrays.reduce((sum, x) => { console.log(`concat ${sum} and ${x}`); return sum.concat(x); }); // → concat 1,2,3 and 4,5 // → concat 1,2,3,4,5 and 6 // → [1, 2, 3, 4, 5, 6]
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
const loop = (initVal, test, update, body) => { let count = 0; for (let i = initVal; test(i); i = update(i)) { body(i); } }; loop(3, n => n > 0, n => n - 1, console.log); // → 3 // → 2 // → 1
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
版本1:
function every(array, test) { for (let x of array) { if (!test(x)) return false; } return true; } console.log(every([1, 3, 5], n => n < 10)); // → true console.log(every([2, 4, 16], n => n < 10)); // → false console.log(every([], n => n < 10)); // → true
版本2:
function every(array, test) { return !array.some(x => !test(x)); } console.log(every([1, 3, 5], n => n < 10)); // → true console.log(every([2, 4, 16], n => n < 10)); // → false console.log(every([], n => n < 10)); // → true
--- -- ------------ - - - -- - -- - - - - - --- - - - ---- -- --
PS. 要用到示例代码中的数据。
const textToCodes = (text) => { let result = []; for (let char of text) { result.push(char.codePointAt(0)); } return result; }; const codeToDirection = (code) => { let result = null; for (let script of SCRIPTS) { if (script.ranges.some(([from, to]) => { return code >= from && code < to; })) { result = script.direction; break; } } return result; }; const elementCounts = (arr) => { let result = []; for (let element of arr) { let index = result.findIndex(a => a.label == element); if (index == -1) { result.push({label: element, count: 1}); } else { result[index].count++; } } return result; }; function dominantDirection(text) { let derections = textToCodes(text).map(code => codeToDirection(code)); let derectionCounts = elementCounts(derections).filter(x => x.label != null); return derectionCounts.reduce((a, b) => { return a.count > b.count ? a : b; }).label; } console.log(dominantDirection("Hello!")); // → ltr console.log(dominantDirection("Hey, مساء الخير")); // → rtl