我正在用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类型以获得匹配的类型?
1 回答
要将字符串转换为大写,
有关您的代码的一般反馈,
(逻辑错误)
toCaps
有两个参数,(1)爆炸字符串,(2)空列表 . 但是你在空列表中调用爆炸字符串acc
和模式匹配nil
/h::t
;你可能想要它反过来 .(类型错误)您编写
toCaps ((acc::t), ...)
,这意味着将acc
列表放在另一个列表t
前面 . 但是acc
本身就是与t
相同的列表;列表只能包含相同类型的元素,因此它们不能包含自己类型的元素 .您不需要嵌套let-expressions;一个let-expression可以有多个声明:
将字符串转换为字符列表,在该列表上递归,并将列表转换回字符串,效率很低,但在列表递归中是一个很好的学习练习 . 这是您的代码的修订版本:
这个功能不是tail-recursive;对于很长的字符串,
upper
对自身的调用最终可能会耗尽堆栈内存 . 您可以通过仅使用函数参数作为临时存储来进行尾调用并将累积结果保存在堆内存中来避免这种情况:缺点是当你将
c::cs
中的第一个字符推入acc
的前面时,它们会以相反的顺序结束,你需要在插入它之前再次反转结果 .无论哪种方式,顶部呈现的仅字符串解决方案使用较少的内存,因为它只需要创建与输入相同大小的单个字符串并循环输入字符串的索引 .