【问题标题】:Javascript streams library - implementationJavascript 流库 - 实现
【发布时间】:2019-03-07 09:26:01
【问题描述】:

java 流(或其他语言的任何其他函数库)非常好。

例如,您可以拥有 (js sudo code)。

Stream.of([1, 2, 3]).filter(x => x > 2).map(x => x * 5).result(); // [15]

忽略语法或具体实现,这只是一个例子。

现在我的问题是流程有点复杂。

例如,如果我需要像这样的每个步骤的不同数据:

Stream.of([1,2, 3])
  .map(x => x * 3)
  .zip([4, 5, 6])
  .map(..//here i need the initial array)
  .map(..//here i need the zipped array)
  .total(..//

正如您所见,在某些方法中我需要最后计算的值,在某些方法中我需要初始值。

此外,在某些情况下,我需要中间值,但在计算它们之后。

map(x => x * 1).map(x => x * 2).map(x => x * 4).map(..//i need the result from 2nd map (x*2)

这是一个愚蠢的例子,但说明了问题。

这个问题有没有好的解决办法。

我认为我可以将所有数据保存在对象中,但这会导致代码更加冗长,因为在每一步我都必须设置并从对象中获取属性。

另一个例子: 对数字求和:[1, 2, 3, 4] -> 10 2以上的过滤器号:[1, 2, 3, 4] -> [3, 4] 将每个数字与总和相乘:[30, 40]

  Stream.of([1,2,3, 4])
    .sum()
    .filter(// here will be the sum, but i want the initial array and later the sum)
    .map(// here i want the filtered array and the calculated sum)

谢谢

【问题讨论】:

  • 哪个库/语言在这里提供zip?它肯定不是 JDK 的默认设置。另外,如果需要初始数组,可以选择稍后实现之前的map
  • Stream.js 但问题就在于此。例如,如果我想映射初始数组,则将结果压缩到另一个数组中,并将结果与​​初始数组或中间结果进行映射。这可以是数组,例如元素的总和。据我了解,每一步我都会失去以前的价值,所以如果以后想得到它,我可以这样做。谢谢
  • 我在底部添加了我想要实现的示例
  • 这是 javascript 而不是 java?
  • Javascript - 将标签和标题更改为 javascript

标签: javascript functional-programming


【解决方案1】:

如果需要中间结果,则保存计算:

const initial = Stream.of([1,2,3,4]);
const total   = initial.sum();
const result  = initial.filter(x => x > 2).map(x => x * total);

上面的示例是编写此类代码的最合乎逻辑的方式。我不明白你为什么要写这样的代码:

Stream.of([1,2,3, 4])
    .sum()
    .filter(/* here will be the sum, but i want the initial array and later the sum */)
    .map(/* here i want the filtered array and the calculated sum */)

您的示例令人困惑和误导。无需链接以函数式编写的代码。

【讨论】:

  • 只是一个简单的问题,因为我不熟悉 JavaScript Stream API。在 Java 中这是行不通的,因为第二行 total = initial.sum() 会消耗流,而第三行会导致异常。这段代码在 JavaScript 中真的正确吗? JavaScript 是否支持 Streams 的多种用途?
  • 这取决于流的实现。如果流以纯函数方式实现,就像大多数流库应该那样,那么它将起作用。我将更新我的答案以说明如何做到这一点。
  • 它不依赖于实现。它在documentation 中:“流的元素在流的生命周期内只被访问一次。就像迭代器一样,必须生成一个新流来重新访问源的相同元素。 "(我假设您在回答中使用了 Java 流,但如果 JavaScript 有一个可以这样工作的第三方库,最好提及它,因为它似乎不在标准 JS 中。)跨度>
  • @DodgyCodeException 这些是 Java 流。我们谈论的是 JavaScript,它没有内置的 Stream 接口。因此,我们可以以任何我们想要的方式实现 Stream 接口,包括纯函数方式。
  • @DodgyCodeException 我认为混淆是因为 OP 在他的问题中提到了 Java 流和 JavaScript 流。然而,他显然想在 JavaScript 中使用流。
【解决方案2】:

我不了解所有语言——我主要是一名 Java 开发人员——但据我了解,不,没有像您正在搜索的那样的灵丹妙药。

在这些类型的库中使用的 Stream 的比喻总是这样的

[input stream] --{read elements one by one}--> [calculate] --> [output stream]

不要将[input stream] 视为集合的视图(尽管在 99% 中它是这样使用的)。更一般地考虑它,例如,一个网络套接字,您可以从中一个一个地读取元素。在你读完一个元素后,它就从网络套接字中消失了,你不能倒带。

所以,基本原理就是

  • 每个元素只读取一次,
  • 所有[calculate] 进程都可能并行运行,并且
  • [calculate] 进程没有副作用

此原则允许库在内部优化和并行化您的计算,这是 Stream API 的主要目标。

所以,回答您的问题:如果您需要多次访问流元素的计算,则需要将原始流或中间结果保存在集合中。

【讨论】:

  • 谢谢,我明白了。所以上面例子的程序代码是这样的: let arr = [1, 2, 3, 4] let sum = arr.reduce((acc,x) => x + acc, 0) let filtered = arr.filter (x => x > 2) let result = filtered.map(x => x * sum) 如果我想用 Stream 达到相同的结果,它会是这样的吗? Stream.of({nums: [1, 2, 3, 4]}) .map(o => {...o, sum: o.nums.reduce((acc, x) => acc + x, 0 )}) .map(o => {...o, 过滤:o.nums.filter(x => x > 2)}) .map(o => o.filtered.map(x => x * o .sum))
  • 你可能会这样写,但这将是对恕我直言概念的严重误用。仅仅因为您有一个概念(在这种情况下为流),并不意味着您需要用它编写所有内容。我认为编程的熟练程度在于使用正确的工具、库、API,是的,还有针对手头任务的编程风格或范例。有关在过程上下文中使用流的良好折衷方案,请参阅 Aadit 的另一个答案。
猜你喜欢
  • 1970-01-01
  • 2019-04-22
  • 1970-01-01
  • 2017-09-18
  • 2012-02-04
  • 2017-06-02
  • 1970-01-01
  • 2021-05-07
  • 2017-06-22
相关资源
最近更新 更多