有没有办法将Node流作为可迭代的?

时间:2012-01-01 22:08:17

标签: javascript node.js functional-programming

我正在编写一个处理传递给stdin的大量文本的工具,每一行都是一个“条目”。我想让我的代码更具功能性,所以我想将这组行视为“序列”或“可迭代”,并使用reduce对其进行迭代。

我目前正在使用Node模块LineStream来处理stdin作为一组行,但它的工作原理是为每一行调度data事件 - 很好,它正在实现Readable Stream界面。

因此,每当data事件触发时,我通过将临时值传递给我的函数来进行非常“手动”的缩减:

var windows = [];

linestream.on('data', function(line) {
  return windows = rollup(windows, extractDate(line), argv.w);
});

linestream.on('end', function() {
  return process.stdout.write(toCsv(windows));
});

process.stdin.resume();

但是做一些像以下方面更有用的功能:

linestream.lines.reduce(rollup, []);

function rollup(windows, line) {
    // would return a new interim or final value
}

当然,我可以将所有行“收集”到一个常规数组然后减少它,但我尝试了它并且当我在一个大型数据集上运行我的工具时它使用了太多的内存 - 所以像迭代这样的东西在溪流上真的是必要的。

我想我要问的是,是否可以编写一个Node函数/模块来执行此操作,或者是否已经存在。

谢谢!

3 个答案:

答案 0 :(得分:1)

我不相信有任何方法可以使它更具功能性,因为你正在处理异步性。

对于linestream.lines存在,我认为需要做以下两件事之一:

  • 你已经说过的每一行的内存缓冲区占用了太多的内存
  • 一种新的语言结构,它允许异步控制流看起来像是同步控制流。

我想你可以这样做(假设使用jquery或其他一些promise api):

var op = (function(){

    var windows = []
        ,done = $.deferred();

    linestream.on('data', function(line) {
      return windows = rollup(windows, extractDate(line), argv.w);
    });

    linestream.on('end', function() {
      process.stdout.write(toCsv(windows));
      return done.resolve(windows);
    });

    process.stdin.resume();

    return done.promise();

})();

但实际上这只是隐藏的东西。

或者,您可以使用Rx之类的内容,或等到generators周围。

答案 1 :(得分:1)

我不太明白rollup应该做什么,但正如其他人所说的那样,你不能拥有一个reduce函数,它希望在没有所有数据的情况下同时拥有所有数据曾经和记忆中。

然而,您可以做的只是在数据事件回调中执行简化逻辑。如果它需要更多状态,例如最后一个值或值的总数,则可以将该数据保留在回调周围的闭包中。

例如,这是异步数字流的滚动平均值。

var total = 0;
var items = 0;
var average;

stream.on('data', function (line) {
  var num = parseInt(line, 10);
  total += line;
  items++;
  average = total / items;
});

stream.on('end', function () {
  console.log("The average is %s", average);
});

在这个例子中,我从每一行中获取相关数据,并保留足够的额外数据以始终了解我的上下文。在这种情况下,我正在计算平均值,因此需要知道有多少项目。

答案 2 :(得分:0)

你已经以功能的方式做到了这一点。你正在监听事件,并在事件触发时运行一个函数,它不能比那更具功能性。

你是第二个例子,不改变功能,它仍然像第一个例子一样功能。然而,它改变的是运行该函数的源。 Reduce依赖于大量数据,同时存储在内存中,正如您所说,这会导致内存占用非常大。

如果我是你,我会继续使用默认节点方式。