从菜场算帐想到的Function Currying

买菜引起的思考

前几天去买菜,老爸说为啥称重的时候,老板一件一件往秤上放,嘴里念叨单价,最后就报总价了。是称了每种商品的价钱,最后心算的总价仍是机器统计的总价?我说确定是机器统计的,就是把数据暂时存起来,最后相加呗。这小功能二十年前就有了吧( ╯□╰ )?!javascript

随后我去淘宝搜索了一下,电子秤价格几十块到一两百,都有置零和累计功能。 java

那么累计功能不就是个最后统计求和吗,这让我想到了函数柯里化,最简单的理解就是一个函数接受参数,最开始不求值,而是到最后才求值(例如参数为空的时候)。咱们能够直接写个具备这个特征的求值函数,这个函数应该会用到闭包去暂存以前输入的参数,结合菜场算帐这个例子就很容易理解。typescript

/** * * @param fn input Function to be currified. when fn has no param?(you can define your call condition), fn would be really called. * otherwise, cache its args in closure. */
function currify(fn: Function) {
  let args: any[] = [];
  return function(arg?:any) {  // 这就是咱们currify 以后的函数定义,在参数不为空的时候存起来,参数为空就call fn根据全部参数求值。
    if (arguments.length === 0) {
      console.log(`ready2call fn with args ${args}`);
      return fn.call(this, args);
    } else {
      [].push.apply(args, arguments);
    }
  }
}
 
function sum(prices: number[]): number {
  return prices.reduce((prev: number, cur: number) => {
    return prev + cur;
  }, 0);
}

let currifiedSum = currify(sum);  // sum 求和函数就是fn,被currify以后新函数具备最后求值的特性

currifiedSum(10);
currifiedSum(20);
currifiedSum(40);
console.warn(currifiedSum());

复制代码

这个currify函数挺有意思的,实际上相似于装饰器,让新的currifiedSum 函数具备了最后求值的功能,这是sum函数原来不具备的特性。结合typescript的装饰器,其实能够写成更简洁的方式:设计模式

@currying()
sum(prices: number[]): number {
    // 函数定义
}
复制代码

扩展每阶段求和

那么以前这个需求比较简单,就是参数为空统计求和。那实际的电子秤是在每类物品称量阶段接受两个参数,一个是物品单价(price),一个是物品重量(weight),计算当前物品的价钱。在某顾客全部物品称重结束时点击累计按钮,触发统计功能。闭包

那简单修改下,让现有的sum 函数具备这个功能吧。app

function extCurrify(fn: Function) {
  let args: any[] = [];
  return function(arg?: any, arg2?: any) {
    if (arguments.length === 0) {
      console.log(`ready2call fn with args ${args}`);
      return fn.call(this, args);
    }
    if (arguments.length === 2) {
      // if has weight & price
      [].push.call(args, calcPrice.call(this, arguments[0], arguments[1]));
    } else {
      [].push.apply(args, arguments);
    }
  }
}

function calcPrice(input: number, price: number): number {
  console.warn(`cur price: ${input * price}`);
  return input * price;
}

let currifiedSum = extCurrify(sum);

currifiedSum(10, 2.2); // weight & price
currifiedSum(1, 30);
currifiedSum(2, 2);
console.warn(currifiedSum());
复制代码

这样加个条件,就能够支持每一个阶段再作个乘法,最后求和。虽然破坏了柯里化函数的通用性结构。 代码比较简单,可是我以为这个生活例子颇有趣,好理解,因此写下来。其中闭包、高阶函数的应用,能够达成装饰器的设计模式,在尽可能不破坏currify函数的通用性前提下,把逻辑代码单独出来为 sum,或者其余功能函数。函数

相关文章
相关标签/搜索