Ruby 创建递归目录的优雅方法
我有一个类似于下面所示的目录结构。它深入到6个级别,在某些情况下,需要在同一级别创建两个目录。我想递归地创建这些目录Ruby 创建递归目录的优雅方法,ruby,Ruby,我有一个类似于下面所示的目录结构。它深入到6个级别,在某些情况下,需要在同一级别创建两个目录。我想递归地创建这些目录 pennsylvania bucks medicine pro bio a b c mental a b c physical a b
pennsylvania
bucks
medicine
pro
bio
a
b
c
mental
a
b
c
physical
a
b
c
non_pro
bio
a
b
c
mental
a
b
c
physical
a
b
c
一种丑陋且可能有缺陷的方法是这样的:
unless File.exists?("pennsylvania")
Dir.mkdir "pennsylvania"
end
unless File.exists?("pennsylvania/bucks")
Dir.mkdir "pennsylvania/bucks"
end
unless File.exists?("pennsylvania/bucks/medicine")
Dir.mkdir "pennsylvania/bucks/medicine"
end
unless File.exists?("pennsylvania/bucks/medicine/pro")
Dir.mkdir "pennsylvania/bucks/medicine/pro"
end
等等。当我们沿着需要创建的目录结构向下导航时,您可以看到这是多么低效。我正在寻找一个更优雅的解决方案。大概是这样的:
unless File.exists?("pennsylvania")
Dir.mkdir "pennsylvania"
end
unless File.exists?("pennsylvania/bucks")
Dir.mkdir "pennsylvania/bucks"
end
unless File.exists?("pennsylvania/bucks/medicine")
Dir.mkdir "pennsylvania/bucks/medicine"
end
unless File.exists?("pennsylvania/bucks/medicine/pro")
Dir.mkdir "pennsylvania/bucks/medicine/pro"
end
使用FileUtils类(虽然不是标准库的一部分)会使它更好:
['pro', 'non_pro'].each do |lev1|
['bio', 'mental', 'physical'].each do |lev2|
['a', 'b', 'c'].each do |lev3|
FileUtils.mkdir_p "pennsylvania/bucks/medicine/#{lev1}/#{lev2}/#{lev3}"
end
end
end
FileUtils的另一个好处是,如果目录已经存在,它不会引发异常;如果目录已经存在,它似乎不会覆盖目录(以及其中的文件)
我提出的第二个解决方案是对第一个解决方案的重大改进。但是还有更优雅的方式吗?是一个不错的选择,它是标准库的一部分
您可以使用生成树
levels = ['pro', 'non_pro'].product(
['bio', 'mental', 'physical'],
['a', 'b', 'c']
)
然后,您可以使您的块与任何数量的级别一起工作
请注意,尽管这非常优雅,但它并不是最有效的方法。问题是每次调用
FileUtils.mkdir\u p
都会尝试创建每个子目录,如果它得到错误,请检查它是否已经存在。对于快速文件系统上的少量目录,这很好。但对于大型树或速度较慢的文件系统(如网络文件系统),这可能会影响性能
为了更有效地使用文件系统,您应该执行类似于这种递归的操作
levels = [
['pennsylvania'],
['bucks'],
['medicine'],
['pro', 'non_pro'],
['bio', 'mental', 'physical'],
['a', 'b', 'c']
]
def make_subdirs(levels, base = [])
return if levels.empty?
levels[0].each { |dir|
new_base = [*base, dir]
mkdir_ignore_if_exists(new_base)
make_subdirs(levels[1..-1], new_base)
}
end
private def mkdir_ignore_if_exists(dirs)
Dir.mkdir(dirs.join("/"))
rescue Errno::EEXIST
end
make_subdirs(levels)
有时,优雅比效率更好,如果它能增强可读性。@Donato优雅的代码在运行五分钟后就不优雅了。性能所需的复杂性可以封装,正如我在上面所做的那样。就像我说的,这只是一个缓慢文件系统(例如SSHFS)或真正巨大的目录树(在这一点上你应该考虑数据库)的问题。有一种更优雅的方法吗?使用数据库?