我有一个ID批次(子列表)的列表,我想遍历此列表并为ID批次中的每个ID生成一个工作进程。这些工作者中的每一个都将查询某些服务,获取结果并将其发送回呼叫者。简单来说,我想将Integer.MAX_VALUE
的列表映射到借助这些System.out.println(Integer.MAX_VALUE + 1); // -2147483648
System.out.println(Integer.MAX_VALUE + 1 == Integer.MIN_VALUE); // true
获得的数据列表。我设法实现了这一目标,但我相信这是一种独特的方式:
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
,而在我的情况下,我也想保留列表的形状。在Haskell中,有一个方便的带有foreach()
函数的类型类ok
,它可以做到这一点:运行Traversable
并且同时允许您对每个项目执行操作(效果)。在Erlang中有类似的东西吗? (例如traverse
?)。
答案 0 :(得分:3)
与Haskell不同,Erlang不是 pure 功能编程语言。因此,它不会对功能是否产生副作用施加任何限制。在Haskell中,即使I / O子系统也无法打破其纯度,这就是为什么在Traversable
和Functor
(traverse
和fmap
)的类型级别上存在区别的原因前者可以对容器的每个元素产生影响,而后者则不能。在Erlang中,没有明显的区别,因此,您可能拥有一个函数execute(Container) ->
,并且仅通过凝视其签名就不知道它是否会产生效果。这就是为什么在Erlang中使用map
和smap
(或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()
之间的主要区别。当以上述方式使用它们时,我通常将它们视为Haskell对monad的理解。
答案 1 :(得分:1)
我喜欢@Oleksandr的答案,但是在列表理解中使用begin..end块会有点脏。我会为此使用函数。
同样重要的是要注意,他回答的第二部分并不能保证尊重原始列表的顺序(即,它只会具有相同的元素数,但是会根据它们到达的顺序进行排序)。可能对您来说很好,但是如果您希望能够匹配输入(Ids)和输出(Results),则必须使用选择性接收,如下所述。
因此,这就是我不使用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.
请务必注意,您在此处未处理任何错误,这一点也很重要。由于您没有捕获出口,因此在生成的过程中出现错误只会杀死主要出口。