将命令行参数写入SML中的文件

将命令行参数写入SML中的文件,sml,smlnj,ml,Sml,Smlnj,Ml,我正在尝试将SML程序中的命令行参数写入一个文件,每个参数位于单独的一行。如果在命令行上运行sml main.sml a b c easy as 1 2 3,所需的输出将是一个包含以下内容的文件: a b c easy as 1 2 3 但是,我从SML获得以下输出: $ sml main.sml a b c easy as 1 2 3 val filePath = "/Users/Josue/Desktop/espi9890.txt" : string val args = ["a","b"

我正在尝试将
SML
程序中的命令行参数写入一个文件,每个参数位于单独的一行。如果在命令行上运行
sml main.sml a b c easy as 1 2 3
,所需的输出将是一个包含以下内容的文件:

a
b
c
easy
as
1
2
3
但是,我从SML获得以下输出:

$ sml main.sml a b c easy as 1 2 3
val filePath = "/Users/Josue/Desktop/espi9890.txt" : string
val args = ["a","b","c","easy","as","1","2","3"] : string list
main.sml:4.21错误:语法错误:插入EQUALOP /usr/local/smlnj/bin/sml:致命错误--未捕获的异常编译,在处引发“语法错误” ../compiler/Parse/main/smlfile.sml:15.24-15.46

使用此代码:

val filePath = "/Users/Josue/Desktop/espi9890.txt";
val args = CommandLine.arguments();

fun writeListToFile x =
    val str = hd x ^ "\n";
    val fd = TextIO.openAppend filePath;
    TextIO.output (fd, str);
    TextIO.closeOut fd;
    writeListToFile (tl x);
| fun writeListToFile [] =
    null;

writeListToFile args;

我遗漏了什么吗?

嵌套值声明的正确语法是:

fun writeListToFile (s::ss) =
    let val fd = TextIO.openAppend filePath
        val _ = TextIO.output (fd, s ^ "\n")
        val _ = TextIO.closeOut fd
    in writeListToFile ss end
  | writeListToFile [] = ()
就是

  • (错误)您忘记了
    让。。。在里面结束

  • (错误)您的第二个模式,
    []
    ,将永远不会匹配,因为第一个模式,
    x
    ,更通用,并且匹配所有输入列表(包括空列表)。因此,即使您的语法错误已修复,此函数也会循环直到崩溃,因为您试图获取空列表的
    hd
    /
    tl

  • (错误)当一个函数有多个匹配案例时,只有第一个案例前面必须加上
    fun
    ,其余必须加上
    |
    。(您可以自由决定如何缩进。)

  • (错误)SML中有两种分号:一种用于分隔声明,另一种是丢弃第一个操作数的值(但不是效果)的运算符。第一种分隔声明的方法总是可以避免的。第二种是您试图使用的一种,以链接多个表达式,每个表达式都具有所需的(文件I/O)效果(相当于在一行中具有多个有效声明的let表达式,如上所述)

    但是。。。在顶层(例如在函数体中),SML无法区分这两种分号之间的区别,因为它们可能都出现在那里。毕竟,我们想要避免的第一类标记函数体的结束,而第二类仅标记函数体中子表达式的结束

    避免这种歧义的方法是包装
    运算符,例如在
    中的
    结束之间或括号内

  • (错误)让此函数返回
    null
    没有意义。您可能认为
    nil
    (空列表,也称为
    []
    ),但是
    val null:'a list->bool
    是一个函数!实际上,这个函数有一个返回值是荒谬的。如果有的话,它可能是一个bool,指示行是否被成功写入(在这种情况下,您可能需要处理IO异常)。最接近不返回任何内容的函数是返回类型单位(值为
    ()
    )的函数

  • (建议)您可以使用
    hd
    /
    tl
    拆分列表,但也可以使用模式匹配。使用模式匹配,就像我给出的例子一样

  • (建议)您可以使用分号,而不是
    val.=…
    声明;也;这只是品味的问题。例如:

    fun writeListToFile (s::ss) =
        let val fd = TextIO.openAppend filePath
        in TextIO.output (fd, s ^ "\n")
         ; TextIO.closeOut fd
         ; writeListToFile ss
        end
      | writeListToFile [] = ()
    
  • (建议)每次函数调用自身时,它都会打开文件、追加和关闭文件,这是相当愚蠢的。理想情况下,您只需打开和关闭文件一次:

    fun writeListToFile lines =
        let val fd = TextIO.openAppend filePath
            fun go [] = TextIO.closeOut fd
              | go (s::ss) = ( TextIO.output (fd, s ^ "\n") ; go ss )
        in go lines end
    
  • >P>(建议),因为你对列表中的每个元素都做同样的事情,你也可以考虑使用一个高阶函数来概括迭代。通常,这将是一个
    val映射:('a->'b)->'a list->'b list
    ,但是由于
    TextIO.output
    返回一个单位,非常类似的
    val应用程序:('a->unit)->'a list->unit
    甚至更好:

    fun writeListToFile lines =
        let val fd = TextIO.openAppend filePath
        in List.app (fn s => TextIO.output (fd, s ^ "\n")) lines
         ; TextIO.closeOut fd
        end
    
  • (建议)最后,您可能希望调用此函数
    appendListFile
    ,或者简单地调用
    appendLines
    ,并将
    filePath
    作为函数的参数,因为
    filePath
    意味着它是一个文件,并且该函数会向每个
    s
    添加换行符。名字很重要

    fun appendLines filePath lines =
        let val fd = TextIO.openAppend filePath
        in List.app (fn s => TextIO.output (fd, s ^ "\n")) lines
         ; TextIO.closeOut fd
        end
    

  • 嵌套值声明的正确语法为:

    fun writeListToFile (s::ss) =
        let val fd = TextIO.openAppend filePath
            val _ = TextIO.output (fd, s ^ "\n")
            val _ = TextIO.closeOut fd
        in writeListToFile ss end
      | writeListToFile [] = ()
    
    就是

  • (错误)您忘记了
    让。。。在里面结束

  • (错误)您的第二个模式,
    []
    ,将永远不会匹配,因为第一个模式,
    x
    ,更通用,并且匹配所有输入列表(包括空列表)。因此,即使您的语法错误已修复,此函数也会循环直到崩溃,因为您试图获取空列表的
    hd
    /
    tl

  • (错误)当一个函数有多个匹配案例时,只有第一个案例前面必须加上
    fun
    ,其余必须加上
    |
    。(您可以自由决定如何缩进。)

  • (错误)SML中有两种分号:一种用于分隔声明,另一种是丢弃第一个操作数的值(但不是效果)的运算符。第一种分隔声明的方法总是可以避免的。第二种是您试图使用的一种,以链接多个表达式,每个表达式都具有所需的(文件I/O)效果(相当于在一行中具有多个有效声明的let表达式,如上所述)

    但是。。。在顶层(例如在函数体中),SML无法区分这两种分号之间的区别,因为它们可能都出现在那里。毕竟,我们想要避免的第一类标记函数体的结束,而第二类仅标记函数体中子表达式的结束

    避免这种歧义的方法是包装
    运算符,例如在
    中的
    结束之间,或在括号内。