[英]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.