`Stream.each/2` 和 `Stream.map/2` 之间的区别?

Difference between `Stream.each/2` and `Stream.map/2`?

它们具有相同(或等效)的类型规格:

each(Enumerable.t(), (element() -> term())) :: Enumerable.t()
map(Enumerable.t(), (element() -> any())) :: Enumerable.t()

根据文档,类型 termany 是等价的:

除了 intended/expected 用法(each/2 提到了副作用)之外,文档没有指出任何明显的区别:

对于简单的例子,它们似乎是等价的:

iex(5)> Stream.each( [1, 2, 3], &(&1) ) |> Enum.to_list()
[1, 2, 3]
iex(8)> Stream.map( [1, 2, 3], &(&1) ) |> Enum.to_list() 
[1, 2, 3]

each/2 文档中的示例似乎也适用于 map/2:

iex(9)> stream = Stream.map([1, 2, 3], fn x -> send(self(), x) end)
#Stream<[enum: [1, 2, 3], funs: [#Function<48.50989570/1 in Stream.map/2>]]>
iex(10)> Enum.to_list(stream)
[1, 2, 3]
iex(11)> receive do: (x when is_integer(x) -> x)
1
iex(12)> receive do: (x when is_integer(x) -> x)
2
iex(13)> receive do: (x when is_integer(x) -> x)
3

如果 Stream.each/2Stream.map/2 不同 – 我 猜测 他们是 – 什么是证明这些差异的简单示例(可能是在 iex 中轻松 运行)?它们是否也以不太明显(例如更难证明)的方式存在差异?

Stream.map/2 转换 可枚举:它 RETURNS 其操作的结果。 Stream.each/2 不修改可枚举;它仅对发射 side-effects 有用。

iex> Stream.map([1, 2, 3], fn x -> x * 2 end) |> Enum.to_list()
[2, 4, 6]
iex> Stream.each([1, 2, 3], fn x -> x * 2 end) |> Enum.to_list()
[1, 2, 3]

what is a simple example demonstrating those differences (that could be easily run in iex)?

这是一个将数字平方然后转换为字符串的流:

iex> 1..10 |> Stream.map(&(&1*&1)) |> Stream.map(&Integer.to_string/1) |> Enum.to_list()
["1", "4", "9", "16", "25", "36", "49", "64", "81", "100"]

可以看到第2张图使用了第1张图的转换输出

但是,如果我们将 map 中的任何一个切换为 each,则不会转换流并且流会保留其先前的输出:

不是平方,而是转换成字符串:

iex> 1..10 |> Stream.each(&(&1*&1)) |> Stream.map(&Integer.to_string/1) |> Enum.to_list()
["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]

平方,但未转换为字符串:

iex> 1..10 |> Stream.map(&(&1*&1)) |> Stream.each(&Integer.to_string/1) |> Enum.to_list()
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Do they differ in less obvious (e.g. harder to demonstrate) ways as well?

不,但是使用 each 而不生成 side-effect 不是预期的 use-case。 each 的真正好处是做一些事情,比如检查阶段之间的当前值:

iex> inspect_even = fn x -> IO.puts("#{x} is #{if rem(x, 2) == 0, do: "even", else: "odd"}") end
iex> 1..3 |> Stream.each(inspect_even) |> Stream.map(&(&1 * 2)) |> Stream.each(inspect_even) |> Enum.to_list
1 is odd
2 is even
2 is even
4 is even
3 is odd
6 is even
[2, 4, 6]