Вывод каждого элемента списка строк в отдельной строке в виде строки - sml - PullRequest
0 голосов
/ 16 октября 2018

Я новичок в SML и, по меньшей мере, расстраиваюсь.

Я использую тип данных, который я назову entry

datatype entry =
    File of string
  | Directory of string * contents
withtype contents = entry list

, чтобы помочь мне создать составнойшаблон файловой директории, такой как

val files =
  Directory("d1",
    [ File "f1",
      Directory("d2",
        [ File "f2",
          Directory("d3",[File "f3"])
        ]),
      File "f4",
      Directory("d3",[File "f5"])
    ]);

Я хочу создать взаимно рекурсивные функции (о чем я только что узнал), которые будут печатать каждую запись в файлах в отдельной строке.По сути, вывод такой:

d1
f1
...
f5

Я пробовал это:

fun print_entries (File s) = [s] (* I've even tried s^"\n" but that only gets me "f#\n" for each file *)
|   print_entries (Directory(s, contents)) = s::(print_contents contents)
and
print_contents nil = nil
|   print_contents (e::es) = print_entries e @ (print_contents es)

, но выводит только список записей.Спасибо за любую помощь.

Ответы [ 2 ]

0 голосов
/ 16 октября 2018

Учитывая ваш тип записи,

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")) ()
0 голосов
/ 16 октября 2018

Ваша функция print_entries / print_contents в настоящее время создает список, который можно легко распечатать:

fun print_line s = (print s; print "\n")

List.app print_line (print_entries files)

В противном случае вы можете переопределить его для прямой печати файлов:

fun print_entries (File s) = print_line s
|   print_entries (Directory(s, contents)) = (print_line s; print_contents contents)
and print_contents [] = ()
|   print_contents (e::es) = (print_entries e; print_contents es)

Структура та же, но вместо использования :: и @ для рекурсивного построения списка вы используете императивные команды (print) и последовательность (;).

Aнезначительное примечание: нет необходимости использовать withtype в определении entry:

datatype entry =
  File of string
| Directory of string * entry list
...