function returns TextIO.elem 选项应该是字符串选项
function returns TextIO.elem option when it should be string option
我正在编写一个函数,该函数接受文件名和读取文件时要替换的字符对列表。我目前在我的一项帮助功能上遇到错误。
prac.sml:177.5-182.12 Error: right-hand-side of clause doesn't agree with function result type [tycon mismatch]
expression: string option -> string * string -> unit
result type: TextIO.elem option -> string * string -> unit
这是给出错误的函数。我不明白到底是什么导致了这种情况发生,谁能帮我看看出了什么问题?
fun echoFile (infile) (c) (x,y) =
if isSome c
then (
printChar (valOf c) (x,y);
echoFile infile (TextIO.input1 infile) (x,y)
) else ()
这里是 printChar 函数:
fun printChar (c) (x,y) =
if x = c
then print y
else print c
这里是调用它的函数。
fun fileSubst _ [] = ()
| fileSubst inputFile ((x,y)::xs) =
let
val infile = TextIO.openIn inputFile
in
echoFile infile TextIO.input1(infile) (x,y);
TextIO.closeIn(infile);
fileSubst inputFile xs
end
以下是对您编写的代码的一些反馈:
函数 TextIO.input1
具有类型 TextIO.instream → TextIO.elem 选项 。当您 inspect the TextIO structure(例如通过在 sml 提示符中写入 open TextIO;
)时,您将找到定义 type elem = char
。因此,将输出视为 char 而不是 string。您可以使用 char → string 类型的函数 str
。但是考虑使用行缓冲,因为一次读取一个字符的文件在系统调用和分配方面是昂贵的。
我已经删除了unnecessary semicolons:fun
、val
之后的那些声明只需要在REPL中得到立竿见影的效果。表达式之间的;
是一个运算符。
我删除了不必要的括号。在构造元组 ((x,y)
) 和声明优先级时确实需要括号。例如,echoFile infile (TextIO.input1 infile) (x,y)
表示 echoFile
是一个具有三个参数的函数,第二个参数是 TextIO.input1 infile
,它本身是一个应用于参数的函数。但是您不需要第二对括号来表示函数应用程序。也就是说,TextIO.input1 infile
和 TextIO.input1(infile)
一样好,就像你每次有数字 42
.
都懒得写 (42)
一样
这意味着您在fileSubst
这一行中仍然存在错误:
echoFile infile TextIO.input1(infile) (x,y)
因为这被解释为 echoFile
有四个参数:infile
、TextIO.input1
、(infile)
和 (x,y)
。看起来 TextIO.input1
和 (infile)
粘在一起,因为没有 space 间隙,但是函数应用程序被认为是函数在其参数前面的定位, 不是 括号的存在。此外,函数应用程序关联到左侧,因此如果我们在上面的行中添加显式括号,它将变为:
(((echoFile infile) TextIO.input1) (infile)) (x,y)
为了克服左结合性,我们写:
echoFile infile (TextIO.input1 infile) (x,y)
这被解释为(粗括号是明确的):
((echoFile infile)
(
TextIO.input1 infile
)
) (x,y)
您的函数 fileSubst
似乎应该用字符 y
替换每个出现的字符 x
。我可能将其称为 "file map",因为它非常类似于 (char → char) → string → string 类型的库函数 String.map
。无论您保留 (x,y) 映射列表还是 char → char 函数都非常相似。
我可能会写一个函数 fileMap
类型 (char → char) → instream → outstream 类似于 String.map
:
fun fileMap f inFile outFile =
let fun go () =
case TextIO.inputLine inFile of
NONE => ()
| SOME s => ( TextIO.output (outFile, String.map f s) ; go () )
in go () end
然后使用它,例如喜欢:
fun cat () = fileMap (fn c => c) TextIO.stdIn TextIO.stdOut
fun fileSubst pairs =
fileMap (fn c => case List.find (fn (x,y) => x = c) pairs of
NONE => c
| SOME (x,y) => y)
关于这些的一些想法:
当类似函数的参数可以是 files 或 filenames 时,我希望区别是在变量名中更清楚。例如。 inputFile
与 infile
不适合我。我宁愿有例如inFile
和 filePath
.
函数是应该采用文件路径还是 instream,我想,取决于您希望如何编写它。因此,像 fileMap
这样非常通用的函数可能采用 instream / outstream,但它也可能采用文件路径。如果您正在制作这两种类型的函数,最好通过名称区分它们或将它们分成不同的模块。
您可能想要处理任意 outstream,而不仅仅是 TextIO.stdOut
,因为您正在处理任意 instreams 也是。您总是可以像 cat
.
中的特殊情况标准 input/output
我在fileMap
里面做了一个辅助函数go
来处理递归。在这种情况下,我也可以不用 fileMap
直接调用自身:
fun fileMap f inFile outFile =
case TextIO.inputLine inFile of
NONE => ()
| SOME s => ( TextIO.output (outFile, String.map f s)
; fileMap f inFile outFile )
因为 fileMap
不会在附加参数中累积任何状态。但通常情况下,递归函数需要额外的参数来保持它们的状态,同时,我不想污染函数的类型签名(就像你的 echoFile
的 c
).这是 monad 的主要用例。
而不是 List.find
上的 case-of,我本可以使用各种库函数来处理 NONE
/SOME
发现于 Option
:
local
val getOpt = Option.getOpt
val mapOpt = Option.map
val find = List.find
in
fun fileSubst pairs =
fileMap (fn c => getOpt (mapOpt #2 (find (fn (x,y) => x = c) pairs), c))
end
我正在编写一个函数,该函数接受文件名和读取文件时要替换的字符对列表。我目前在我的一项帮助功能上遇到错误。
prac.sml:177.5-182.12 Error: right-hand-side of clause doesn't agree with function result type [tycon mismatch]
expression: string option -> string * string -> unit
result type: TextIO.elem option -> string * string -> unit
这是给出错误的函数。我不明白到底是什么导致了这种情况发生,谁能帮我看看出了什么问题?
fun echoFile (infile) (c) (x,y) =
if isSome c
then (
printChar (valOf c) (x,y);
echoFile infile (TextIO.input1 infile) (x,y)
) else ()
这里是 printChar 函数:
fun printChar (c) (x,y) =
if x = c
then print y
else print c
这里是调用它的函数。
fun fileSubst _ [] = ()
| fileSubst inputFile ((x,y)::xs) =
let
val infile = TextIO.openIn inputFile
in
echoFile infile TextIO.input1(infile) (x,y);
TextIO.closeIn(infile);
fileSubst inputFile xs
end
以下是对您编写的代码的一些反馈:
函数
TextIO.input1
具有类型 TextIO.instream → TextIO.elem 选项 。当您 inspect the TextIO structure(例如通过在 sml 提示符中写入open TextIO;
)时,您将找到定义type elem = char
。因此,将输出视为 char 而不是 string。您可以使用 char → string 类型的函数str
。但是考虑使用行缓冲,因为一次读取一个字符的文件在系统调用和分配方面是昂贵的。我已经删除了unnecessary semicolons:
fun
、val
之后的那些声明只需要在REPL中得到立竿见影的效果。表达式之间的;
是一个运算符。我删除了不必要的括号。在构造元组 (
都懒得写(x,y)
) 和声明优先级时确实需要括号。例如,echoFile infile (TextIO.input1 infile) (x,y)
表示echoFile
是一个具有三个参数的函数,第二个参数是TextIO.input1 infile
,它本身是一个应用于参数的函数。但是您不需要第二对括号来表示函数应用程序。也就是说,TextIO.input1 infile
和TextIO.input1(infile)
一样好,就像你每次有数字42
.(42)
一样这意味着您在
fileSubst
这一行中仍然存在错误:echoFile infile TextIO.input1(infile) (x,y)
因为这被解释为
echoFile
有四个参数:infile
、TextIO.input1
、(infile)
和(x,y)
。看起来TextIO.input1
和(infile)
粘在一起,因为没有 space 间隙,但是函数应用程序被认为是函数在其参数前面的定位, 不是 括号的存在。此外,函数应用程序关联到左侧,因此如果我们在上面的行中添加显式括号,它将变为:(((echoFile infile) TextIO.input1) (infile)) (x,y)
为了克服左结合性,我们写:
echoFile infile (TextIO.input1 infile) (x,y)
这被解释为(粗括号是明确的):
((echoFile infile)
(
TextIO.input1 infile
)
) (x,y)
您的函数
fileSubst
似乎应该用字符y
替换每个出现的字符x
。我可能将其称为 "file map",因为它非常类似于 (char → char) → string → string 类型的库函数String.map
。无论您保留 (x,y) 映射列表还是 char → char 函数都非常相似。
我可能会写一个函数 fileMap
类型 (char → char) → instream → outstream 类似于 String.map
:
fun fileMap f inFile outFile =
let fun go () =
case TextIO.inputLine inFile of
NONE => ()
| SOME s => ( TextIO.output (outFile, String.map f s) ; go () )
in go () end
然后使用它,例如喜欢:
fun cat () = fileMap (fn c => c) TextIO.stdIn TextIO.stdOut
fun fileSubst pairs =
fileMap (fn c => case List.find (fn (x,y) => x = c) pairs of
NONE => c
| SOME (x,y) => y)
关于这些的一些想法:
当类似函数的参数可以是 files 或 filenames 时,我希望区别是在变量名中更清楚。例如。
inputFile
与infile
不适合我。我宁愿有例如inFile
和filePath
.函数是应该采用文件路径还是 instream,我想,取决于您希望如何编写它。因此,像
fileMap
这样非常通用的函数可能采用 instream / outstream,但它也可能采用文件路径。如果您正在制作这两种类型的函数,最好通过名称区分它们或将它们分成不同的模块。您可能想要处理任意 outstream,而不仅仅是
TextIO.stdOut
,因为您正在处理任意 instreams 也是。您总是可以像cat
. 中的特殊情况标准 input/output
我在
fileMap
里面做了一个辅助函数go
来处理递归。在这种情况下,我也可以不用fileMap
直接调用自身:fun fileMap f inFile outFile = case TextIO.inputLine inFile of NONE => () | SOME s => ( TextIO.output (outFile, String.map f s) ; fileMap f inFile outFile )
因为
fileMap
不会在附加参数中累积任何状态。但通常情况下,递归函数需要额外的参数来保持它们的状态,同时,我不想污染函数的类型签名(就像你的echoFile
的c
).这是 monad 的主要用例。而不是
List.find
上的 case-of,我本可以使用各种库函数来处理NONE
/SOME
发现于Option
:local val getOpt = Option.getOpt val mapOpt = Option.map val find = List.find in fun fileSubst pairs = fileMap (fn c => getOpt (mapOpt #2 (find (fn (x,y) => x = c) pairs), c)) end