【问题标题】:How to convert algorithm to functional programming?如何将算法转换为函数式编程?
【发布时间】:2019-01-23 03:14:54
【问题描述】:

我对函数式编程和长生不老药完全陌生,即使在学习了语法之后,我也很难集中精力解决不变性问题。

考虑以下我用 python 编写的简单算法。它接收树的级别列表。每个级别都是一个节点列表,其中一个节点包含一个 id、一个(最初)为空的子节点列表和指向其父节点的指针。它对树进行排序,使得每个父母在他的孩子列表中都有他的孩子并返回根。例如,以下输入:

[[{"id": 10,
    "children": [],
    "parent_id": null}],
  [{"id": 12,
    "children": [],
    "parent_id": 10},
   {"id": 18,
    "children": [],
    "parent_id": 10},
   {"id": 13,
    "children": [],
    "parent_id": 10}],
  [{"id": 17,
    "children": [],
    "parent_id": 12},
   {"id": 16,
    "children": [],
    "parent_id": 13},
   {"id": 15,
    "children": [],
    "parent_id": 12}]}]

将转换为以下输出:

[{"id": 10,
  "children":
   [{"id": 12,
     "children":
      [{"id": 17,
        "children": [],
        "parent_id": 12},
       {"id": 15,
        "children": [],
        "parent_id": 12}],
     "parent_id": 10},
    {"id": 18,
     "children": [],
     "parent_id": 10},
    {"id": 13,
     "children":
      [{"id": 16,
        "children": [],
        "parent_id": 13}],
     "parent_id": 10}],
  "parent_id": null}]

代码:

def build_tree(levels):
            ids_to_nodes = []
            for i in range(len(levels)):
                    level = levels[i]
                    ids_to_nodes.append({})
                    for node in level:
                            ids_to_nodes[i][node["id"]] = node

                    if i > 0:
                            for node in level:
                                    levels[i - 1][node["parent_id"]]["children"].append(node)

            return levels[0].values()

我在 elixir 中最接近实现这一点的是

def fix_level(levels, ids_to_nodes, i) do
      if i < length(levels) do
              level = Enum.at(levels, i)
              new_level =
              Enum.reduce level, %{},  fn node, acc ->
                Map.put(acc, node["id"], node)
              end
              ids_to_nodes = ids_to_nodes ++ [new_level]

              if i > 0 do
                Enum.reduce level, Enum.at(ids_to_nodes, i - 1)[node["parent_id"]], fn node, acc ->
                  Map.put(acc, "children", Enum.at(ids_to_nodes, i - 1)[node["parent_id"]]["children"] ++ [node]) # Doesn't work coz creates new map
                end
              end

              fix_level(params, ids_to_nodes, i + 1)
      end
      Map.values(ids_to_nodes[0])
    end


  def fix(levels) do
    fix_level(levels, ids_to_nodes, 0)
  end

我知道代码在很多地方都非常低效,尤其是在列表的末尾 - 但我不确定如何以有效的方式重写它们,更重要的是,我完全被阻止了标记的线。我认为我以命令式/面向对象的方式思考太多了。帮助理解函数式编程将不胜感激。

【问题讨论】:

  • 您能否提供示例性输入和所需的输出?
  • 在编辑中提供。谢谢

标签: python elixir


【解决方案1】:

尝试使用递归而不是循环,从没有 parent_id(或 nil)的节点开始,递归地为其每个子节点构建子树。

下面的代码很简单,但大部分是不言自明的。

它获取当前 parent_id 的子节点(根节点为 nil)并为其每个子节点构建子树。

%{地图 | key1: val1, key2: val2} 是 Elixir 更新地图的简写

defmodule TestModule do
    def build_tree(levels, parent_id \\ nil) do
        levels
        |> Enum.filter(& &1.parent_id == parent_id) # get children of parent_id
        |> Enum.map(fn level ->
            %{level | children: build_tree(levels, level.id)} # recursively build subtrees of current level
        end)
        # we should now have child nodes of parent_id with their children populated
    end
end

# sample input
levels = [
    %{id: 1, parent_id: nil, children: []},
    %{id: 2, parent_id: 1, children: []},
    %{id: 3, parent_id: 2, children: []},
    %{id: 4, parent_id: 2, children: []},
    %{id: 5, parent_id: 4, children: []},
    %{id: 6, parent_id: 1, children: []},
    %{id: 7, parent_id: 6, children: []},
    %{id: 8, parent_id: 6, children: []},
    %{id: 9, parent_id: nil, children: []},
    %{id: 10, parent_id: 9, children: []},
]

TestModule.build_tree(levels)

【讨论】:

  • 您能否详细说明一下为什么以及如何工作? “试试这个”非常神秘,不一定对 OP 有帮助。
  • @Máté 添加了更多细节
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 2022-08-20
  • 2013-03-02
  • 2019-11-27
  • 1970-01-01
  • 2013-10-11
  • 1970-01-01
相关资源
最近更新 更多