我从练习中做了polindrom_products exersice并发现了有趣的东西。
第一版:
@doc """
Generates all palindrome products from an optionally given min factor (or 1) to a given max factor.
"""
@spec generate(non_neg_integer, non_neg_integer) :: map()
def generate(max_factor, min_factor \\ 1) do
palindromes = for a <- min_factor..max_factor,
b <- a..max_factor,
prod = a*b,
palindrome?(prod),
do: {prod, [a, b]}
Enum.reduce(palindromes, %{}, fn {prod, pair}, acc ->
Map.update(acc, prod, [pair], &(&1 ++ [pair]))
end)
end
def palindrome?(n) do
s = Integer.to_string(n)
s == String.reverse(s)
end
第二版:
@doc """
Generates all palindrome products from an optionally given min factor (or 1) to a given max factor.
"""
@spec generate(non_neg_integer, non_neg_integer) :: map()
def generate(max_factor, min_factor \\ 1) do
palindromes = for a <- min_factor..max_factor,
b <- a..max_factor,
prod = a*b,
"#{prod}" == String.reverse("#{prod}"),
do: {prod, [a, b]}
Enum.reduce(palindromes, %{}, fn {prod, pair}, acc ->
Map.update(acc, prod, [pair], &(&1 ++ [pair]))
end)
end
第一个版本比第二个版本快6倍左右。替换"#{prod}" == String.reverse("#{prod}")
上的Integer.to_string(prod) == String.reverse(Integer.to_string(prod))
可获得收益。
例如,使用此测试:
test "smallest palindromes from triple digit factors" do
palindromes = Palindromes.generate(999, 100)
assert palindromes |> Dict.keys |> Enum.sort |> hd == 10201
assert palindromes[10201] == [[101, 101]]
end
第一个版本以0.7秒执行,第二个版本以4秒执行。我正在使用Elixir v1.1.1并在本地计算机上运行Sublime Text的代码。
这是什么原因?
答案 0 :(得分:3)
首先,我总是对Elixir计划报告的计时结果持谨慎态度。除非您从代码中进行基准测试,否则您将包括BEAM VM的启动时间以及可能会显着扭曲结果的小代码。
然而,问题归结为原因
"#{prod}" == String.reverse("#{prod}")
比
慢得多s = Integer.to_string(n)
s == String.reverse(s)
让我们拉开第一行
"#{prod}"
- &GT;这实际上是2个函数,内部函数使用to_string协议将prod转换为字符串,然后进行字符串插值。因为它使用协议,而不是直接的Integer.to_string 在合并协议之前,它可能会变慢。
您在同一行中执行此操作两次,因此您最多需要4次函数调用。添加String.reverse和==,这是6个函数调用。
第二个版本正确缓存结果,只执行3个功能 调用。
如果您想进一步研究这些微基准测试,我强烈推荐使用benchfella库。 https://github.com/alco/benchfella