繁体   English   中英

如何将算法转换为函数式编程?

[英]How to convert algorithm to functional programming?

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

考虑我用 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

我知道代码在很多地方效率很低,特别是在列表的末尾 - 但我不确定如何以有效的方式重写它们,更重要的是,我完全被标记线挡住了. 我认为我以命令式/面向对象的方式想得太多。 帮助理解函数式编程将不胜感激。

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

下面的代码很简单,但主要是不言自明的。

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

%{地图 | 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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM