列表:在 Erlang 中具有副作用的映射



我有一个 id 的批处理(子列表)列表,我想遍历这个列表并为该批 id 中的每个 id 生成一个工作进程。这些工作线程中的每一个都将查询一些服务,获取结果并将其发送回调用方。简而言之,我想将id列表映射到我通过这些id获得的数据列表。我设法实现了这一目标,但我认为这是一种非惯用的方式:

lists:map(fun(Ids) ->
Pids = [spawn_link(fun() ->
Result = [...] % Here goes a side-effect operation (http request)
Self ! {received_data, process(Result)}
end) || Id <- Ids],
[receive {received_data, Data} -> Data end || _Pid <- Pids],
end, JobChunks)))

在这种情况下,如您所见,我滥用map功能,因为它被设计为无副作用。但我看不到其他选择。有foreach(),但它仅用于运行副作用并且只返回ok而就我而言,我也想保留列表的形状。在Haskell中,有一个方便的类型类Traversable带有traverse函数,它正是这样做的:运行fmap,同时允许您对每个项目执行操作(效果)。二郎有类似的东西吗?(比如smap

与Haskell不同,Erlang不是一种纯粹的函数式编程语言。作为推论,它不会对功能是否会产生副作用进行限制。在Haskell中,即使是I/O子系统也不能破坏它的纯度,这就是为什么在类型级别上存在TraversableFunctor(traversefmap)之间的区别,前者可以对容器的每个元素运行效果,而后者不能。在 Erlang 中没有如此明确的区别,因此,您可能有一个函数execute(Container) ->,并且您不知道它是否会通过凝视其签名来运行效果。这就是为什么在 Erlang 中拥有mapsmap(或traverse,或任何你称之为它)没有意义,也不会带来任何价值。但是,确实,将lists:map用于此类操作会破坏map契约,而该契约应该是纯函数。在这种情况下,我可能会建议您使用列表理解,在我看来这是一种更惯用的方式:

[begin
Pids = [spawn_link(fun() ->
% Side-effect operation which worker performs
end) || Id <- Ids],
[receive {received_data, Data} -> Data end || _Pid <- Pids]
end || Ids <- JobChunks].

再次在我自己的观点中,副作用是列表理解和lists:map()之间的主要区别。当它们以上述方式使用时,我通常认为它们是哈斯克尔的单子理解。

我喜欢@Oleksandr答案,但使用开头。列表理解中的结束块感觉有点。我会为此使用函数。

同样重要的是要注意,他的回答的第二部分并不保证尊重原始列表的顺序(即它只有相同的#元素,但它们将根据它们到达的顺序进行排序)。这对你来说可能没问题,但如果你想能够匹配输入(Ids)和输出(结果),你必须使用选择性接收,我将在下面向您展示。

所以,这就是我在没有 OTP 的情况下实现它的方式(因为你也没有使用 OTP):

your_function() ->
[process_chunk(Ids) || Ids <- JobChunks].
process_chunk(Ids) ->
Pids = [spawn_side_effect_fun(Id) || Id <- Ids],
[get_result_for(Pid) || _Pid <- Pids].
spawn_side_effect_fun(Id) ->
Self = self(),
spawn_link(fun() ->
Self ! {received_data, self(), your_side_effect_operation()}
end).
get_result_for(Pid) ->
receive
%% Here we're pattern-matching on Pid
%% so that we get the result for this particular Pid
%% therefore the order is preserved in the final list.
{received_data, Pid, Data} -> Data
end.

同样重要的是要注意,您在此处没有处理任何错误。由于您没有捕获出口,因此生成进程中的错误只会杀死主要退出。

相关内容

  • 没有找到相关文章

最新更新