`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()
根据文档,类型 term
和 any
是等价的:
除了 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/2
和 Stream.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]
它们具有相同(或等效)的类型规格:
each(Enumerable.t(), (element() -> term())) :: Enumerable.t()
map(Enumerable.t(), (element() -> any())) :: Enumerable.t()
根据文档,类型 term
和 any
是等价的:
除了 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/2
和 Stream.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]