zoukankan      html  css  js  c++  java
  • Eloquent JavaScript #05# higher-order functions

    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

      --- --  ------------ - - - -- - -- - - - - - --- - - -             ----     -- -- 

    ④ 高阶函数forEach,功能类似于for/of循环。

    ["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

      --- --  ------------ - - - -- - -- - - - - - --- - - -             ----     -- -- 

    ⑧ 高阶函数some

    [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

      --- --  ------------ - - - -- - -- - - - - - --- - - -             ----     -- -- 

    ⑨ 高阶函数findIndex

    [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

    4、重写课本示例代码(统计文本中字符的脚本归属分布)

    数据: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

    ① Flattening

    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]

      --- --  ------------ - - - -- - -- - - - - - --- - - -             ----     -- -- 

    ② Your own loop

    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

      --- --  ------------ - - - -- - -- - - - - - --- - - -             ----     -- -- 

    ③ Everything

    版本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

      --- --  ------------ - - - -- - -- - - - - - --- - - -             ----     -- -- 

    ④ Dominant writing direction

    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
  • 相关阅读:
    用BAT使用FTP命令上传文件
    BAT自动复制最新插件至运行程序
    requests模块源码阅读总结
    Lucene查询语法汇总
    Ansible scp Python脚本
    4.2 rust 命令行参数
    4.1 python中调用rust程序
    冒泡排序
    Golang开发命令行工具之flag包的使用
    MySQL基于Binlog的数据恢复实战
  • 原文地址:https://www.cnblogs.com/xkxf/p/9552654.html
Copyright © 2011-2022 走看看