【问题标题】:Outputting each element of a string list on its own line as a string - sml将字符串列表的每个元素在其自己的行上作为字符串输出 - sml
【发布时间】:2019-03-20 09:17:43
【问题描述】:

我是 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)

但它只输出条目列表。感谢您的所有帮助。

【问题讨论】:

    标签: sml smlnj ml


    【解决方案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
    

    或者您可以使用List.map 来处理entries 列表:

    fun names (File name) = [name]
      | names (Directory (name, entries)) =
          name :: List.concat (List.map names entries))
    

    由于List.mapnames <entry> 的每次调用都会生成一个名称列表,因此List.map names entries 会生成一个名称列表 列表。使用List.concat 将其展平为单个名称列表。

    这有点像相互递归,但entryentry list 之间的相互依赖嵌入在传递给List.mapnames 函数中,列表递归由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")) ()
    

    【讨论】:

      【解决方案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)和排序(;)。

      小提示:entry的定义中不需要使用withtype

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

      【讨论】:

      • 感谢您的帮助!抱歉,除了这个,我还处理了其他一些任务。
      猜你喜欢
      • 1970-01-01
      • 2014-01-24
      • 2022-01-19
      • 1970-01-01
      • 2015-05-15
      • 1970-01-01
      • 2017-07-24
      • 1970-01-01
      • 2023-03-23
      相关资源
      最近更新 更多