在自己的行上将字符串列表的每个元素作为字符串输出-sml

时间:2018-10-15 21:59:01

标签: sml smlnj ml

我是SML的新手,至少可以说感到沮丧。

我使用一种数据类型,我将其称为条目

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 :(得分:2)

您的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)和排序(;

一个小注释:withtype的定义中无需使用entry

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

答案 1 :(得分:1)

给出您的输入类型,

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将其拼凑成一个单一的名称列表。

这有点像相互递归,但是entryentry list之间的相互依赖关系已嵌入传递给names的{​​{1}}函数中,并且处理了列表递归由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