【问题标题】:Dynamic programming - translating JS to Elixir动态编程——将 JS 翻译成 Elixir
【发布时间】:2021-11-16 22:17:01
【问题描述】:

我正在尝试将以下玩具动态编程问题转换为 Elixir,但考虑到 Elixir 没有提前返回,我很难了解如何做到这一点。

它应该从“数字”返回一个有效的组合,总和为“targetSum”

const howSum = (targetSum, numbers) => {

  if (targetSum === 0) return [];
  if (targetSum < 0) return null;

  for (let num of numbers) {
    const remainder = targetSum - num;
    const remainderResult = howSum(remainder, numbers);
    if (remainderResult !== null) {
      return [...remainderResult, num];
    }
  }

  return null;

}

console.log(howSum(7, [2, 3])) // [3,2,2]

我可以低于 Elixir 版本以使用列表理解记录所有可能的解决方案,但我怎样才能让函数返回找到的第一个解决方案并在该点返回/停止?

defmodule HowSum do
  @doc """
  Can you make target_sum from numbers list
  You can use individual numbers as many times as you like
  """
  def sum(0, _numbers, _), do: []
  def sum(target_sum, _numbers, _) when target_sum < 0, do: nil

  def sum(target_sum, numbers, path) do
    for number <- numbers do
      remainder = target_sum - number
      result = sum(remainder, numbers, path ++ [number])

      if result == [] do
        IO.inspect(path ++ [number])
      end
    end
  end
  
end

更新

这是我的解决方案,它看起来并不惯用,但可以使用代理 :-)

defmodule HowSum do
  def cache do
    Agent.start_link(fn -> nil end, name: :solution)
  end

  @doc """
  Can you make target_sum from numbers list
  You can use individual numbers as many times as you like
  """
  def sum(0, _numbers, _), do: []
  def sum(target_sum, _numbers, _) when target_sum < 0, do: nil

  def sum(target_sum, numbers, path) do
    solution = Agent.get(:solution, & &1)

    if !solution do
      for number <- numbers do
        remainder = target_sum - number
        result = sum(remainder, numbers, path ++ [number])

        if result == [] do
          Agent.update(:solution, &(&1 = path ++ [number]))
        end
      end
    end

    Agent.get(:solution, & &1)
  end
end

【问题讨论】:

标签: elixir dynamic-programming


【解决方案1】:

Enum.find/2 / Enum.find_index/2 / Enum.find_value/2 对于这种情况非常有用,因为您希望在“循环”中的某个时间点返回。 Enum.reduce_while/3 对于需要一些累加器和提前返回的更通用的算法很有用。

这是一个基于您的 javascript 实现使用 Enum.find_value/2 的解决方案:

defmodule HowSum do
  def sum(target_sum, number) do
    target_sum |> do_sum(number) |> Enum.reverse()
  end
  
  defp do_sum(0, _numbers), do: []
  defp do_sum(target_sum, _numbers) when target_sum < 0, do: nil

  defp do_sum(target_sum, numbers) do
    Enum.find_value(numbers, fn number ->
      remainder = target_sum - number
      result = do_sum(remainder, numbers)

      if result != nil do
        [number | result]
      end
    end)
  end
end


HowSum.sum(7, [2, 3])  # [3, 2, 2]

请注意,这是highly inefficient,通过在末尾追加来构建列表,因为您需要在每个步骤中克隆它。所以我用[number | result] 替换了result ++ [number],将递归函数移动为私有do_ 函数,并在其结果上调用Enum.reverse/1。这在使用递归函数时很常见。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 2015-01-13
    • 1970-01-01
    • 2018-02-08
    • 1970-01-01
    • 2015-10-28
    相关资源
    最近更新 更多