JavaScript - 递归调用部分应用函数

时间:2015-04-21 07:07:05

标签: javascript functional-programming

我正在用JavaScript(Node.js)编写文件处理器。简化的伪代码如下。对于文件中的每一行,它打印到输出,如果该行是包含指示符,则递归处理包含的文件。

function fileProcessor (file) {
  // read lines from file
  lines.forEach( function (line) {
    // #1 - include processor:
    if line matches "#include includefile"
      fileProcessor(includefile);
    // #2 - output processor:
    // write line to output
  });
}

下一步我想通过解耦文件处理器和线路处理器来改进其设计。我熟悉OO,它应该看起来像下面的类图。但现在我想用FP方式尝试。

定义一个返回fileProcessor的函数,应用哪些行处理器。

var fileProcessorGen = function (lineProcessors) {
  return function (file) {
    // read lines from file
    lines.forEach( function (line) {
      lineProcessors.forEach( function (processor) {
        processor.call(this, line);
      });
    });
  };
};

我的目的是实现关注点的分离。在这个例子中,只有两个行处理器,但实际上会有更多。调用者将根据其目的选择一些线路处理器。因此,我不会将它们硬编码到文件处理器或任何线路处理器中。

var fileProcessor = fileProcessorGen([includeProcessor, outputProcessor]);

现在我的问题是如何在include处理器中调用fileProcessor(部分应用程序)?

function includeProcessor(line) {
  if line matches "#include includefile"
    fileProcessor(includefile);  // <--- how to get fileProcessor ?
};

function outputProcessor(line) {
  // write line to output
}

也许我必须将fileProcessor作为参数传递给includeProcessor,但是如何?

function includeProcessor(line, fileProcessor) {
  if line matches "#include includefile"
    fileProcessor(includefile);
};

var fileProcessorGen = function (lineProcessors) {
  return function (file) {
    // read lines from file
    lines.forEach( function (line) {
      lineProcessors.forEach( function (processor) {
        processor.call(this, line, ???);  // <-- how to pass the parameter?
      });
    });
  };
};

2 个答案:

答案 0 :(得分:1)

您只需要为匿名函数指定一个名称,然后您可以在函数范围内通过该标识符引用它,无论您为该函数指定什么标识符。 (我几乎总是建议给出函数名,甚至函数表达式。不仅仅是为了能够引用它们而是为了更容易读取堆栈跟踪)。

function fileProcessorFactory (lineProcessors) {
  return function fileProcessor (file) {
    // read lines from file
    lines.forEach( function (line) {
      lineProcessors.forEach( function (processor) {
        processor.call(this, line, fileProcessor );
      });
    });
  };
};

var fileProcessorInstance = fileProcessorFactory( [ includeProcessor, outputProcessor ] );

但是你当然应该意识到你当前的解决方案是非常低效的,因为它每次被调用时都会创建全新的函数集,如果你只是声明它,你可以使它更简单,更有效。功能一次,让他们互相打电话。

function fileProcessor ( file ) {
    //split those lines
    lines.forEach( lineProcessor );
}

function lineProcessor ( line ) {
    if line matches "#include includefile"
        fileProcessor( includefile );
    else
        outputProcessor( line );
}

function outputProcessor ( line ) {
   //...
}

如果你想支持多个指令,只需在一个名为&#34;一个javascript对象&#34;的散列图中查找它们的名字。如果名称不在地图中,则抛出错误。 (还有ES6中的实际Map数据类型,您也可以使用它)。

var directives = {
     include : function includeDirective ( expression ) {
     },
     define : function defineDirective ( expression ) {
     },
     //...
};

你也可以在没有正则表达式的情况下完全执行此操作,因为预处理器通常只遵循3个超简单规则(伪代码)

  1. 如果是第一个字母&#34;#&#34; =是指令
  2. while not space =指令名称
  3. 而不是换行=表达
  4. 澄清功能创建问题。 javascript的功能性将函数分离为函数声明和函数表达式。函数声明被计算一次,每次控件到达包含它的行时,都会计算函数表达式,从而产生一个新函数,您也可以将它存储在变量中。如果你不存储它就会变成垃圾(例如在你传递给它的函数返回之后)。

    lines.forEach( function (line) { // creates one new function
      lineProcessors.forEach( function (processor) { //creates one new function per line
        processor.call(this, line);
      });// one function per line is now garbage
    }); // the outer function is now garbage
    

答案 1 :(得分:0)

您可以创建一个新的fileProcessor实例,然后在includeProcessor中使用它。

function includeProcessor(line) {
  if line matches "#include includefile"
  { 
    var fp = fileProcessorGen([includeProcessor, outputProcessor]);
    fp(includefile);  
  }
};