首页 文章

将字符串转换为大写

提问于
浏览
0

我正在用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 回答

  • 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 的前面时,它们会以相反的顺序结束,你需要在插入它之前再次反转结果 .

    无论哪种方式,顶部呈现的仅字符串解决方案使用较少的内存,因为它只需要创建与输入相同大小的单个字符串并循环输入字符串的索引 .

相关问题