如何基于子目录大小计算更新父目录大小(Elixir实现)
如何基于子目录大小计算更新父目录大小(Elixir实现)
嘿,我明白你现在的困境了——你已经拿到了每个目录的初始大小,想把所有子目录的大小累加进父目录里,但Elixir的不可变特性让你的代码没达到预期效果,对吧?先别着急,我们一步步来解决这个问题。
先分析你的问题根源
你原来的代码里尝试用递归和Enum.map更新父目录大小,但忽略了Elixir的核心特性:变量不可变。你在if块里写的system = system ++ [...]只是创建了一个局部的新变量,并没有把更新后的列表传递到递归调用里,最后自然返回了初始的空列表。而你用IO.inspect能看到中间结果,只是因为那个局部变量确实被创建了,但没有被保留下来而已。
解决方案思路
要实现需求,我们需要确保先处理最深的子目录,再处理父目录——这样当我们更新父目录大小时,所有子目录的大小已经被累加进去了。具体步骤是:
- 把目录列表按路径长度从长到短排序(比如先处理
["/", "a", "e"],再处理["/", "a"],最后处理["/"])。 - 遍历每个目录,找到它的父目录(路径去掉最后一个元素),然后将当前目录的大小加到父目录的大小上。
- 利用
Enum.reduce来累积更新后的目录列表,因为reduce天生适合这种需要逐步累积结果的场景。
具体代码实现
首先,写一个辅助函数来更新单个父目录的大小:
def update_parent_size(system, child_dir) do parent_path = List.delete_at(child_dir.path, -1) # 根目录没有父目录,直接返回原列表 if parent_path == [] do system else Enum.map(system, fn dir -> if dir.path == parent_path do # 不可变更新:创建新的map,size加上子目录的大小 %{dir | size: dir.size + child_dir.size} else dir end end) end end
然后,主函数来协调整个更新流程:
def calculate_dir_sizes(initial_system) do # 按路径长度降序排序,确保先处理最深的子目录 sorted_dirs = Enum.sort_by(initial_system, &length(&1.path), :desc) # 用reduce累积更新后的目录列表 Enum.reduce(sorted_dirs, initial_system, fn dir, acc -> update_parent_size(acc, dir) end) end
测试一下效果
用你给出的初始数据测试:
initial = [ %{path: ["/", "a", "e"], size: 584}, %{path: ["/", "a"], size: 94269}, %{path: ["/", "d"], size: 24933642}, %{path: ["/"], size: 23352670} ] calculate_dir_sizes(initial)
返回的结果就是你想要的:
[ %{path: ["/", "a", "e"], size: 584}, %{path: ["/", "a"], size: 94853}, %{path: ["/", "d"], size: 24933642}, %{path: ["/"], size: 48381165} ]
额外小建议
你提到构建树结构可能更适合这个AoC挑战——确实,如果一开始就用树来存储目录结构,后续计算大小会更直观(比如递归遍历树,累加子节点的大小)。不过既然你已经走到这一步,上面的方法完全可以解决当前的问题。
备注:内容来源于stack exchange,提问作者shmink
相关产品推荐
相关产品推荐

