天天看點

JS偏函數、組合函數、緩存函數

JS偏函數、組合函數、緩存函數

一、JavaScript代碼

[附件]/functools.js

(function( window ) { 

"use strict"; 

var functools = window.functools || {}; 

/** 

 * partial function 

 */ 

functools.curry = function( f, ctx ) { 

    var args = Array.prototype.slice.call( arguments, 2 ); 

    return function() { 

        var newArgs = args.concat(Array.prototype.slice.call( arguments )); 

        return f.apply( ctx, newArgs ); 

    } 

 * right-left partial function 

functools.rcurry = function( f, ctx ) { 

        var newArgs = Array.prototype.slice.call( arguments ).concat( args ); 

 * composite function 

 * @description `compose(f, g, x)(y) = f(g(y), x)` 

functools.compose = function( f, g, f_ctx, g_ctx ) { 

    var args_for_f = Array.prototype.slice.call( arguments, 4 ); 

        var args_for_g = Array.prototype.slice.call( arguments ); 

        var mid = g.apply( g_ctx, args_for_g ); 

        args_for_f.unshift(mid); 

        return f.apply( f_ctx, args_for_f); 

 * Memoizer, and allows multiple arguments. 

 * @description Recursion needs to override function literal.  

 *              e.g. `func = memoize( func, null )` 

functools.memoize = function( f, ctx ) { 

    var memo = {}; 

        var key = Array.prototype.join.call(arguments, "_"); 

        var res = memo[ key ]; 

        if ( res === undefined ) { 

            res = f.apply( ctx, arguments ); 

            memo[ key ] = res; 

        } 

        return res; 

 * Return the result and runtime of the function. 

functools.timeit = function( f, ctx ) { 

    var started = +new Date(); 

    var result = f.apply( ctx, args ); 

    var runtime = +new Date() - started; 

    return { 

        result: result, 

        runtime: runtime 

function extend( a, b ) { 

    for ( var prop in b ) { 

        if ( b[ prop ] === undefined ) { 

            delete a[ prop ]; 

        // Avoid "Member not found" error in IE8 caused by setting window.constructor 

        } else if ( prop !== "constructor" || a !== window ) { 

            a[ prop ] = b[ prop ]; 

    return a; 

if ( window.functools === undefined ) { 

    extend( window, functools ); 

    window.functools = functools; 

// get at whatever the global object is, like window in browsers 

}( (function() {return this;}.call()) )); 

二、QUnit測試代碼

[附件]/functools.html

<!DOCTYPE html> 

<html> 

<head> 

    <meta charset="utf-8"> 

    <title>functools test case</title> 

    <link rel="stylesheet" href="./qunit/qunit-1.11.0.css"> 

</head> 

<body> 

    <div id="qunit"></div> 

    <div id="qunit-fixture"></div> 

    <script src="./qunit/qunit-1.11.0.js"></script> 

    <script src="./functools.js"></script> 

    <script> 

        test( "curry test case", function() { 

            function sum() { 

                var sum = 0; 

                for ( var i = 0; i < arguments.length; i++ ) { 

                    sum +=  arguments[i]; 

                } 

                return sum; 

            } 

            var sumBase10 = curry( sum, null, 1, 2, 3, 4 ); 

            var value = sumBase10( 20, 30, 40 ); 

            equal( value, 100, "We expect value to be 100" ); 

        }); 

        test( "rcurry test case", function() { 

            function between( value, low, high, allowEqual ) { 

                if ( value > low && value < high ) { 

                    return true; 

                if ( allowEqual && ( value == low || value == high) ) { 

                return false; 

            var betweenClosed = rcurry( between, null, true ), 

                between10and20 = rcurry( betweenClosed, null, 10, 20 ); 

            ok( between10and20( 10 ) , "10 is between 10 and 20" ); 

            ok( between10and20( 20 ) , "20 is between 10 and 20" ); 

            equal( between10and20( 9 ), false, "9 is not between 10 and 20" ); 

            equal( between10and20( 21 ), false, "21 is not between 10 and 20" ); 

        test( "compose test case", function() { 

            /* only one argument */ 

            function trim( s ) { 

                return s.replace( /(^\s+)|(\s+$)/g, "" ); 

            function upper( s ) { 

                return s.toUpperCase(); 

            var trimUpper = compose( upper, trim, null, null ); 

            var value = trimUpper( "  trim and upper  " ); 

            equal( value, "TRIM AND UPPER", "trimUpper test case" ); 

            /* multiple arguments */ 

            function f( x, y ) { 

                return x + y; 

            var g = f; 

            var fg = compose( f, g, null, null, "F" ); 

            equal( fg( "X", "Y" ), "XYF", "We expect value to be XYF" ); 

        test( "memoize test case", function() { 

            function fib( n ) { 

                if ( n == 0 ) return 0; 

                if ( n == 1 ) return 1; 

                return fib( n - 1) + fib( n - 2 ); 

            var res_old = timeit( fib, null, 30 ); 

            fib = memoize( fib, null ); // override 

            var res_new = timeit( fib, null, 30 ); 

            equal( res_new.result, res_old.result, 

                [ "It's expected value.", res_old.runtime, res_new.runtime ].join("|") 

            ); 

            function func( a, b ) { 

                func.counter++; 

                if ( a == 1 ) return 1; 

                return a + b + func( a - 1, b - 1 ); 

            func = memoize( func, null ); // override 

            func.counter = 0; // declare 

            var a = func( 5, 4 ), // = 25 

                a_cnt = func.counter; // = 5 

            func.counter = 0; // reset 

            var b = func( 6, 5 ), // 6 + 5 + func( 5, 4 ) << cache 

                b_cnt = func.counter; // = 1 

            equal( b, 36, [ "It's expected value.", a_cnt, b_cnt ].join("|") ); 

    </script> 

</body> 

</html> 

ps:附件解壓,打開functools.html即可測試。

<a href="http://down.51cto.com/data/2362319" target="_blank">附件:http://down.51cto.com/data/2362319</a>

     本文轉自winorlose2000 51CTO部落格,原文連結:http://blog.51cto.com/vaero/1138913,如需轉載請自行聯系原作者

繼續閱讀