将命令行参数写入 SML 文件
Write command-line arguments to file in SML
我正在尝试将我的 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 Error: syntax error: inserting EQUALOP
/usr/local/smlnj/bin/sml: Fatal error -- Uncaught exception Compile with "syntax error" raised at
../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 [] = ()
即
(错误) 你忘记了 let ... in ... end
.
(错误) 你的第二个模式 []
永远不会匹配,因为第一个模式 x
更通用并匹配所有输入列表(包括空列表)。因此,即使您的语法错误已修复,此函数也会循环直到崩溃,因为您正在尝试获取空列表的 hd
/tl
。
(Error) 当一个函数有多个匹配案例时,只有第一个必须在前面加上fun
,其余的必须有一个|
代替。 (您可以自由决定如何缩进。)
(Error) SML中分号有两种:一种是分隔声明,一种是丢弃值的操作符(但不是其第一个操作数的效果)。第一种分隔声明的方式总是可以避免的。第二种是您尝试使用的一种,以便链接多个表达式,每个表达式都具有所需的(文件 I/O)效果(并且等效于连续包含多个有效声明的 let 表达式,例如以上)。
但是...在顶层(例如在函数体中),SML 无法区分两种分号之间的区别,因为它们可能都出现在那里。毕竟,我们要避免的第一种标记函数体的结束,而第二种只是标记函数体中子表达式的结束。
避免这种歧义的方法是在不允许声明的地方包装 ;
运算符,例如在 in
和 end
之间,或在括号内。
(错误) 有这个功能没有意义 return null
。您可能在想 nil
(空列表,又名 []
),但 val null : 'a list -> bool
是一个函数!真的,这个函数有一个 return 值是荒谬的。如果有的话,它可能是一个 bool 指示行是否已成功写入(在这种情况下您可能需要处理 IO 异常)。最接近不 return 任何东西的函数是 return 类型 unit(值为 ()
)的函数。
(建议) 可以使用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
(建议) 因为你对列表中的每个元素做同样的事情,你也可以考虑使用高阶函数来概括迭代。通常,那将是一个 val map : ('a -> 'b) -> 'a list -> 'b list
,但由于 TextIO.output
return 是一个 单位 ,非常相似的 val app : ('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
(建议)最后,你可能想调用这个函数appendListToFile
,或者简单地调用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
我正在尝试将我的 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 Error: syntax error: inserting EQUALOP /usr/local/smlnj/bin/sml: Fatal error -- Uncaught exception Compile with "syntax error" raised at ../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 [] = ()
即
(错误) 你忘记了
let ... in ... end
.(错误) 你的第二个模式
[]
永远不会匹配,因为第一个模式x
更通用并匹配所有输入列表(包括空列表)。因此,即使您的语法错误已修复,此函数也会循环直到崩溃,因为您正在尝试获取空列表的hd
/tl
。(Error) 当一个函数有多个匹配案例时,只有第一个必须在前面加上
fun
,其余的必须有一个|
代替。 (您可以自由决定如何缩进。)(Error) SML中分号有两种:一种是分隔声明,一种是丢弃值的操作符(但不是其第一个操作数的效果)。第一种分隔声明的方式总是可以避免的。第二种是您尝试使用的一种,以便链接多个表达式,每个表达式都具有所需的(文件 I/O)效果(并且等效于连续包含多个有效声明的 let 表达式,例如以上)。
但是...在顶层(例如在函数体中),SML 无法区分两种分号之间的区别,因为它们可能都出现在那里。毕竟,我们要避免的第一种标记函数体的结束,而第二种只是标记函数体中子表达式的结束。
避免这种歧义的方法是在不允许声明的地方包装
;
运算符,例如在in
和end
之间,或在括号内。(错误) 有这个功能没有意义 return
null
。您可能在想nil
(空列表,又名[]
),但val null : 'a list -> bool
是一个函数!真的,这个函数有一个 return 值是荒谬的。如果有的话,它可能是一个 bool 指示行是否已成功写入(在这种情况下您可能需要处理 IO 异常)。最接近不 return 任何东西的函数是 return 类型 unit(值为()
)的函数。(建议) 可以使用
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
(建议) 因为你对列表中的每个元素做同样的事情,你也可以考虑使用高阶函数来概括迭代。通常,那将是一个
val map : ('a -> 'b) -> 'a list -> 'b list
,但由于TextIO.output
return 是一个 单位 ,非常相似的val app : ('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
(建议)最后,你可能想调用这个函数
appendListToFile
,或者简单地调用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