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 args = Array.prototype.slice.call( arguments, 2 );
- return function() {
- var newArgs = Array.prototype.slice.call( arguments ).concat( args );
- return f.apply( ctx, newArgs );
- }
- }
- /**
- * 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 );
- return function() {
- 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 = {};
- return function() {
- 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 args = Array.prototype.slice.call( arguments, 2 );
- 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 true;
- }
- 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() {
- /* only one argument */
- 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("|")
- );
- /* multiple arguments */
- 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即可测试。
附件:http://down.51cto.com/data/2362319
本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/1138913,如需转载请自行联系原作者