簡體   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