将字符串转换为大写

Convert string to uppercase

我正在用 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 类型以获得匹配的类型?

trying to take a string and make all the characters capitalized

要将字符串转换为大写,

- 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,一个列表,放在[=20前面=],另一个列表。但是 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 对自身的调用最终可能会耗尽堆栈内存。您可以通过仅制作 tail-calls 并通过使用函数参数作为临时存储将累积结果保存在堆内存中来避免这种情况:

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 的前面时,它们以相反的顺序结束,你需要在内爆之前再次反转结果。

无论哪种方式,顶部显示的 string-only 解决方案使用较少的内存,因为它只需要创建一个与输入大小相同的字符串并循环输入字符串的索引。