我在SML中编程,尝试取一个字符串并使所有字符都大写。我是SML和函数式编程的新手,我不能完全匹配这些类型。我的代码如下所示:
fun allCaps (str) =
let val ex = explode(str)
in
let fun toCaps (acc, nil: char list) = acc
| toCaps (acc, h::t: char list) = toCaps ((acc::t), [Char.toUpper(h)])
in
toCaps(ex, []:char list)
end
end;
口译员给我错误
Error: operator and operand don't agree [tycon mismatch]
operator domain: char * char list
operand: char list * char list
in expression:
toCaps (acc :: t,Char.toUpper h :: nil)
...
toCaps (nil: char list,ex)
对我来说没有任何意义,因为它在处理的函数中看起来非常明确地列出了整个时间。无论如何,我如何初始化一个空的char类型以获得匹配的类型?
答案 0 :(得分:1)
尝试取一个字符串并使所有字符大写
要将字符串转换为大写,
- val allCaps = String.map Char.toUpper;
- allCaps "Hello World!";
> val it = "HELLO WORLD!" : string
有关您的代码的一些一般反馈,
(逻辑错误) toCaps
有两个参数,(1)分解字符串,(2)一个空列表。但是您在空列表中调用爆炸字符串acc
并与nil
/ h::t
进行模式匹配;你可能想要反过来。
(类型错误)您编写toCaps ((acc::t), ...)
,这意味着将acc
(一个列表)放在t
前面,另一个列表。但acc
本身就是与t
相同的列表;列表只能包含相同类型的元素,因此它们不能包含自己类型的元素。
您不需要嵌套let-expressions;一个let-expression可以有多个声明:
fun allCaps s =
let val L = explode s
fun toCaps ...
in ... end
除非提高清晰度,否则不需要输入注释;编译器将推断出类型。
将字符串转换为字符列表,对该列表进行递归,并将列表转换回字符串,效率很低,但在列表递归中是一个很好的学习练习。这是您的代码的修订版本:
fun allCaps s =
let fun upper (c::cs) = Char.toUpper c :: upper cs
| upper [] = []
in implode (upper (explode s)) end
此功能不是tail-recursive;对于很长的字符串,upper
对自身的调用可能最终耗尽堆栈内存。您可以通过仅使用函数参数作为临时存储来进行尾调用并将累积结果保存在堆内存中来避免这种情况:
fun allCaps s =
let fun upper (c::cs, acc) = upper (cs, Char.toUpper c :: acc)
| upper ([], acc) = rev acc
in implode (upper (explode s, [])) end
缺点是,当您将第一个字符从c::cs
推到acc
的前面时,它们会以相反的顺序结束,您需要在将其压缩之前再次反转结果。
无论哪种方式,顶部显示的仅字符串解决方案使用更少的内存,因为它只需要创建一个与输入相同大小的单个字符串并循环输入字符串的索引。