Учитывая ваш тип записи,
datatype entry = File of string | Directory of string * entry list
вы можете либо создать список имен файлов / каталогов путем взаимной рекурсии,
fun names (File name) = [name]
| names (Directory (name, entries)) = name :: names_entries entries
and names_entries [] = []
| names_entries (entry :: entries) = names entry @ names_entries entries
Или вы можете прибегнуть к обработке списка entries
с использованием List.map
:
fun names (File name) = [name]
| names (Directory (name, entries)) =
name :: List.concat (List.map names entries))
Поскольку каждый вызов names <entry>
, выполняемый List.map
, создает список имен, List.map names entries
создает список списков имен,Сведение этого обратно в единый список имен выполняется с помощью List.concat
.
Это похоже на взаимную рекурсию, но взаимная зависимость между entry
и entry list
встроена в функцию names
передается в List.map
и рекурсия списка обрабатывается только List.map
.
Вы также можете получить список имен, свернув элемент файла:
fun cata f acc entry =
case entry of
File name => f (entry, acc)
| Directory (name, entries) =>
let val acc' = f (entry, acc) in
foldl (fn (entry, acc'') => cata f acc'' entry) acc'
end
fun name (File name) = name
| name (Directory (name, _)) = name
val names =
rev o cata (fn (entry, names) => name entry :: names) []
Эта функция полезна для других целей, например, для рекурсивного подсчета количества файлов и каталогов:
fun isFile (File _) = true
| isFile (Directory _) = false
fun isDirectory (Directory _) = true
| isDirectory (File _) = false
val countFilesDirectories =
let fun counter (entry, (numFiles, numDirs)) =
if isFile entry then (numFiles+1, numDirs) else
if isDirectory entry then (numFiles, numDirs+1) else
(numFiles, numDirs)
in cata counter (0,0) end
Или даже для рекурсивной печати имен файлов и каталогов:
val printEntries =
cata (fn (entry, ()) => print (name entry ^ "\n")) ()