2 First-Class Functions and Applicative Programming

7832 단어

Functions as First-Class Things

  • A number can be stored in a variable and so can a function:
  •   var fortytwo = function() { return 42 };
    
  • A number can be stored in an array slot and so can a function:
  • var fortytwos = [42, function() { return 42 }];
    
  • A number can be stored in an object field and so can a function:
  • var fortytwos = {number: 42, fun: function() { return 42 }};
    
  • A number can be created as needed and so can a function:
  • 42 + (function() { return 42 })();
    //=> 84
    
  • A number can be passed to a function and so can a function:
  • function weirdAdd(n, f) { return n + f() }
    weirdAdd(42, function() { return 42 });
    //=> 84
    
  • A number can be returned from a function and so can a function:
  • return 42;
    return function() { return 42 };
    
    _.each(['whiskey', 'tango', 'foxtrot'], function(word) {
     console.log(word.charAt(0).toUpperCase() + word.substr(1));
    });
    // (console) Whiskey
    // (console) Tango
    // (console) Foxtrot
    

    JavaScript's Multiple Paradigms


    Imperative programming Programming based around describing actions in detail Prototype-based object-oriented programming Programming based around prototypical objects and instances of them Metaprogramming Programming manipulating the basis of JavaScript's execution model

    Imperative programming

    var lyrics = [];
    for (var 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 lyricsSegment(n) {
      return _.chain([])
        .push(n + "bottles of beer on the wall")
        .push(n + "bottles of beer")
        .push("Take one down, pass it around")
        .tap(function(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();
    }
    

    Prototype-based object-oriented programming

    var a = {name: "a", fun: function () { return this; }};
    a.fun();
    //=> {name: "a", fun: ...};
    
    var bFunc = function () { return this };
    var b = {name: "b", fun: bFunc};
    b.fun();
    //=> some global object, probably Window
    

    Metaprogramming

    function Point2D(x, y) {
      this._x = x;
      this._y = y;
    }
    
    new Point2D(0, 1);
    //=> {_x: 0, _y: 1}
    
    function Point3D(x, y, z) {
      Point2D.call(this, x, y);
      this._z = z;
    }
    
    new Point3D(10, -1, 100);
    //=> {_x: 10, _y: -1, _z: 100}
    

    Applicative Programming

    var nums = [1,2,3,4,5];
    function doubleAll(array) {
      return _.map(array, function(n) {return n*2});
    }
    
    doubleAll(nums);
    //=> [2, 4, 6, 8, 10]
    
    function average(array) {
      var sum = _.reduce(array, function(a, b) {return a+b});
      return sum / _.size(array);
    }
    
    average(nums);
    //=> 3
    
    /* grab only even numbers in nums */
    function onlyEven(array) {
     return _.filter(array, function(n) {
     return (n%2) === 0;
     });
    }
    onlyEven(nums);
    //=> [2, 4]
    
  • _.map calls a function on every value in a collection in turn, returning a collection of the results
  • _.reduce collects a composite value from the incremental results of a supplied with an accumulation value and each value in a collection
  • _.filter calls a predicate function (one returning a true or false value) and grabs each value where said predicate returned true, returning them in a new collection

  • Collection-Centric Programming

    _.map({a: 1, b: 2}, _.identity);
    //=> [1,2]
    
    _.map({a: 1, b: 2}, function(v, k) {
      return [k,v];
    });
    //=> [['a', 1], ['b', 2]]
    
    _.map({a: 1, b: 2}, function(v,k,coll) {
     return [k, v, _.keys(coll)];
    });
    //=> [['a', 1, ['a', 'b']], ['b', 2, ['a', 'b']]]
    

    Other Examples of Applicative Programming


    reduceRight
    var nums = [100,2,25];
    function div(x,y) {return x/y};
    
    _.reduce(nums, div);
    //=> 2
    
    _.reduceRight(nums, div);
    //=> 0.125
    
    function allOf(/* funs */) {
     return _.reduceRight(arguments, function(truth, f) {
     return truth && f();
     }, true);
    }
    function anyOf(/* funs */) {
     return _.reduceRight(arguments, function(truth, f) {
     return truth || f();
     }, false);
    }
    
    function T() { return true }
    function F() { return false }
    allOf();
    //=> true
    allOf(T, T);
    //=> true
    allOf(T, T, T , T , F);
    //=> false
    anyOf(T, T, F);
    //=> true
    anyOf(F, F, F, F);
    //=> false
    anyOf();
    //=> false
    

    find
    _.find(['a', 'b', '3', 'd'], _.isNumber);
    //=> 3
    

    reject
    _.reject(['a', 'b', 3, 'd'], _.isNumber);
    //=> ['a', 'b', 'd']
    

    all
    _.all([1, 2, 3, 4], _.isNumber);
    //=> true
    

    any
    _.any([1, 2, 'c', 4], _.isString);
    //=> true
    

    sortBy, groupBy, and countBy
    var people = [{name: "Rick", age: 30}, {name: "Jaka", age: 24}];
    _.sortBy(people, function(p) { return p.age });
    //=> [{name: "Jaka", age: 24}, {name: "Rick", age: 30}]
    
    var albums = [{title: "Sabbath Bloody Sabbath", genre: "Metal"},
     {title: "Scientist", genre: "Dub"},
     {title: "Undertow", genre: "Metal"}];
    _.groupBy(albums, function(a) { return a.genre });
    //=> {Metal:[{title:"Sabbath Bloody Sabbath", genre:"Metal"},
    // {title:"Undertow", genre:"Metal"}],
    // Dub: [{title:"Scientist", genre:"Dub"}]}
    
    
    _.countBy(albums, function(a) {return a.genre});
    //=> {Metal: 2, Dub: 1}
    

    Defining a Few Applicative Functions

    function cat() {
     var head = _.first(arguments);
     if (existy(head))
     return head.concat.apply(head, _.rest(arguments));
     else
     return [];
    }
    cat([1,2,3], [4,5], [6,7,8]);
    //=> [1, 2, 3, 4, 5, 6, 7, 8]
    
    function construct(head, tail) {
     return cat([head], _.toArray(tail));
    }
    construct(42, [1,2,3]);
    //=> [42, 1, 2, 3]
    

    Data Thinking

    var zombie = {name: "Bub", film: "Day of the Dead"};
    _.keys(zombie);
    //=> ["name", "film"]
    _.values(zombie);
    //=> ["Bub", "Day of the Dead"]
    
    _.pluck([{title: "Chthon", author: "Anthony"},
     {title: "Grendel", author: "Gardner"},
     {title: "After Dark"}],
     'author');
    //=> ["Anthony", "Gardner", undefined]
    

    Summary

  • They can be stored in a variable.
  • They can be stored in an array slot.
  • They can be stored in an object field.
  • They can be created as needed.
  • They can be passed to other functions.
  • They can be returned from functions.
  • 좋은 웹페이지 즐겨찾기