我正在学习函数式编程,我在elixir中做了一个简单的斐波纳契。
我知道在函数式编程中不可能改变值,我制作了一个代码来制作具有memoization的fibonacci,但代码很糟糕。 如何改进此代码?
defmodule Fib do
def fib_memoized(0, memo) do
{0, memo}
end
def fib_memoized(1, memo) do
{1, memo}
end
def fib_memoized(n, memo \\ %{}) do
if Map.has_key?(memo, n) do
{ memo[n], memo }
else
{n1, memo1} = fib_memoized(n-1, memo)
{n2, memo2} = fib_memoized(n-2, memo1)
value = n1+n2
{value, Map.merge(memo2, %{n => value})}
end
end
def fib(n) do
{ value, _ } = fib_memoized(n)
value
end
end
IO.puts Fib.fib(1000)
在javascript中可以创建一个高阶函数并保存" map"并更新这个。
例如:
function memoization(fn) {
let memo = {}
return function (n) {
if(!memo[n]) {
memo[n] = fn(n)
}
return memo[n]
}
}
有可能做出类似的东西吗?
答案 0 :(得分:2)
您在JS中发布给Elixir的剪辑的逐字翻译将类似于:
memoization = fn fun ->
fn n, acc ->
acc =
if(!acc[n]) do
Process.sleep(1_000)
IO.inspect acc, label: "Just put... Need a rest... Sleeping... Zzzz..."
Map.put(acc, n, fun.(n))
else
acc
end
{acc, Map.get(acc, n)}
end
end
fun = memoization.(& &1 * 2)
{acc, _} = IO.inspect fun.(42, %{}), label: "Result for 42"
{acc, _} = IO.inspect fun.(42, acc), label: "Result for 42"
{acc, _} = IO.inspect fun.(3.14, acc), label: "Result for 3.14"
{_, _} = IO.inspect fun.(3.14, acc), label: "Result for 3.14"
导致:
# Just put... Need a rest... Sleeping... Zzzz...: %{}
# Result for 42: {%{42 => 84}, 84}
# Result for 42: {%{42 => 84}, 84}
# Just put... Need a rest... Sleeping... Zzzz...: %{42 => 84}
# Result for 3.14: {%{42 => 84, 3.14 => 6.28}, 6.28}
# Result for 3.14: {%{42 => 84, 3.14 => 6.28}, 6.28}
由于Elixir中没有全局状态,因此必须通过累加器。哦,等等,确实有!
我已经编写了一份详细的blog post on how to memoize functions in Elixir,其中包含一个示例和即用型代码。
答案 1 :(得分:1)
通常当我必须在elixir中存储状态时,我会启动一个进程来执行此操作。在斐波纳契案例中,Agent
非常适合。
可以这样写:
defmodule Fib do
use Agent
def start do
Agent.start_link(fn -> %{0 => 0, 1 => 1} end, name: __MODULE__)
end
def fib(n) do
cached_value = Agent.get(__MODULE__, &(Map.get(&1, n)))
if cached_value do
cached_value
else
v = fib(n - 1) + fib(n - 2)
Agent.update(__MODULE__, &(Map.put(&1, n, v)))
v
end
end
end
{:ok, _} = Fib.start
IO.puts Fib.fib(1000)