Fibonacci与memoization elixir

时间:2018-05-01 14:20:06

标签: functional-programming elixir fibonacci memoization

我正在学习函数式编程,我在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]
    }
  }

有可能做出类似的东西吗?

2 个答案:

答案 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)