【发布时间】:2016-11-10 21:35:40
【问题描述】:
我目前通过网站 exercism.io 学习 Elixir。 我遇到了“倍数之和”的问题:
如果我们列出不超过 20 的所有自然数 3 或 5 的倍数,我们得到 3、5、6 和 9、10、12、15 和 18。 这些倍数之和是78
我用这段代码解决了这个问题:
defmodule SumOfMultiples do
@doc """
Adds up all numbers from 1 to a given end number that are multiples of the factors provided.
"""
@spec to(non_neg_integer, [non_neg_integer]) :: non_neg_integer
def to(limit, factors) do
1..limit - 1
|> Enum.reduce([], fn(x,acc) ->
is_multiple? = factors
|> Enum.map(&(rem(x,&1) === 0))
|> Enum.any?
if is_multiple?, do: [x|acc], else: acc
end)
|> Enum.sum
end
end
但我最近在 Elixir 中发现了进程,所以我想用多进程来解决这个问题:
defmodule SumOfMultiples do
@doc """
Adds up all numbers from 1 to a given end number that are multiples of the factors provided.
"""
@spec to(non_neg_integer, [non_neg_integer]) :: non_neg_integer
def to(limit, factors) do
me = self
1..limit - 1
|> Enum.map(fn(x) ->
spawn(SumOfMultiples, :is_multiple?, [x, factors, me])
end)
|> Enum.map(fn(_) ->
receive do
{true, n} -> n
{false, n} -> 0
end
end)
|> Enum.sum
end
def is_multiple?(n, factors, pid) do
flag = factors
|> Enum.map(&(rem(n,&1) === 0))
|> Enum.any?
send pid, {flag, n}
end
end
我使用并行地图来解决这个问题。可以,但问题是它的性能比单进程版本低 4 倍。
如果有人能解释一下为什么会有这样的性能差异,那将非常有用,因为我计划用多进程版本解决 exercism.io 问题的其余部分。
谢谢!
--------- 更新---------
感谢您的回答!事实证明你是对的!谢谢你的解释!这是我的新实现:
defmodule SumOfMultiples do
@doc """
Adds up all numbers from 1 to a given end number that are multiples of the factors provided.
"""
@spec to(non_neg_integer, [non_neg_integer]) :: non_neg_integer
def to(limit, factors) do
me = self
1..limit - 1
|> Stream.chunk(200, 200, Stream.cycle([0]))
|> Enum.map(fn(x) ->
spawn(SumOfMultiples, :do_to, [x, factors, me])
end)
|> Enum.map(fn(_) ->
receive do
n -> n
end
end)
|> Enum.sum
end
def do_to(list, factors, pid) do
result = list
|> Enum.reduce([], fn(x,acc) ->
is_multiple? = factors
|> Enum.map(&(rem(x,&1) === 0))
|> Enum.any?
if is_multiple?, do: [x|acc], else: acc
end)
|> Enum.sum
send pid, result
end
end
最大值似乎是 200。现在我比单个进程快 40%!耶!
【问题讨论】:
-
Stream.chunk_by() 是你的朋友
标签: elixir