简体   繁体   English

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

[英]How to convert algorithm to functional programming?

I'm totally new to functional programming and elixir, and even after learning about the syntax, I'm having a hard time wrapping my head around solving issues with immutability.我对函数式编程和 elixir 完全陌生,即使在学习了语法之后,我也很难集中精力解决具有不变性的问题。

Consider the following simple algorithm I wrote in python.考虑我用 python 编写的以下简单算法。 It receives a list of levels of a tree.它接收树的级别列表。 Each level is a list of nodes where a node contains an id, an (initially) empty list of children and pointer to his parent node.每个级别是一个节点列表,其中一个节点包含一个 id、一个(最初)空的子节点列表和指向其父节点的指针。 It orders the tree such that each parent has his children in his children list and returns the root(s).它对树进行排序,使得每个父母在他的孩子列表中都有他的孩子,并返回根。 For example, the following input:例如,以下输入:

[[{"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}]}]

Would be converted to the following output:将转换为以下输出:

[{"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}]

The code:编码:

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()

The closest I got to implementing this in elixir is我最接近在 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

I'm aware that the code is very inefficient in a lot of places, particularly around ends of lists - but I'm not sure how to rewrite them in an efficient manner, and more importantly, I'm totally blocked at the marked line.我知道代码在很多地方效率很低,特别是在列表的末尾 - 但我不确定如何以有效的方式重写它们,更重要的是,我完全被标记线挡住了. I think I'm thinking too much in an imperative / object oriented way.我认为我以命令式/面向对象的方式想得太多。 Help in understanding functional programming will be appreciated.帮助理解函数式编程将不胜感激。

Try to use recursion instead of loops, start at nodes which have no parent_id(or nil) and recursively build subtrees for each of their children.尝试使用递归而不是循环,从没有 parent_id(或 nil)的节点开始,并为每个子节点递归构建子树。

The below code is simplistic but is mostly self-explanatory.下面的代码很简单,但主要是不言自明的。

It gets the children of the current parent_id(which is nil for root nodes) and builds subtrees for each of its child nodes.它获取当前 parent_id 的子节点(根节点为零)并为其每个子节点构建子树。

%{map | %{地图 | key1: val1, key2: val2} is Elixir short-hand for updating maps 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