这需要一些拆包。
首先,你的核心错误:System.Char.ToUpper
是一个函数。它需要一个字符并返回另一个字符。它不会以某种方式将其参数“更新”为新值。
let x = 'a'
let y = System.Char.ToUpper x // y = 'A', x = 'a'.
在上面的代码中,我为y
函数的结果命名。的值y
是'A'
,但 的值x
仍然是'a'
。调用该函数后,x
并没有改变。
从这个错误开始,其余的一切都随之而来。
其次,List.iter
是一个函数,对于列表的每个元素,它都会“发生”一些事情。它不会用新的东西替换列表的每个元素,也不会创建新的列表。它只是让每个元素都发生一些事情。这种“某事”的最简单示例是打印到控制台:
List.iter (fun x -> printfn "%i" x) [1; 2; 3] // Prints "1", then "2", then "3"
请注意,此函数有两个参数:表示需要发生的事情的函数,以及从中获取元素的列表。在您的问题中,您似乎缺少第二个论点。如何List.iter
知道使用哪个列表?
的第一个参数List.iter
需要是一个返回的函数unit
。这是 F# 中的一种特殊类型,基本上表示“无价值”。当一个函数没有返回值时,这意味着调用它的唯一原因是让外部发生一些事情(在函数式编程中称为“副作用”)。这就是为什么List.iter
需要函数返回的原因unit
——它是对意外提供错误函数的额外保护,就像你实际上所做的那样:你提供的函数返回char
。这就是您收到错误的原因。
第三,就像 with 一样ToUpper
,调用List.ofArray
不会以某种方式“更新”x
为列表。相反,它返回一个列表。如果你不给返回的列表命名,它就会丢失。这意味着你打电话的方式List.ofArray
是徒劳的。
您实际需要的是 (1) 获取字符串中的字符序列,然后 (2) 将其转换为每个字符为大写的新序列,然后 (3) 将这些字符重新粘合在一起以获得新字符串。
步骤 (1) 是无操作的,因为 .NET 字符串已经是字符序列(即它们实现了IEnumerable<char>
)。步骤 (2) 是通过称为 的常见操作完成的Seq.map
。这是一种通过将给定函数应用于每个元素来将序列转换为新序列的操作。在这种情况下,“给定函数”将是System.Char.ToUpper
。步骤 (3) 可以通过 完成String.concat
,但您需要先将每个字符转换为字符串,因为String.concat
需要一个字符串序列,而不是字符。
let chars = s
let upperChars = Seq.map System.Char.ToUpper chars
let strChars = Seq.map string upperChars
let result = String.concat "" strChars
或者这可以以更短的方式完成,无需给每个步骤的结果一个单独的名称,而是通过将每个结果直接传递到下一个操作:
let result =
s
|> Seq.map System.Char.ToUpper
|> Seq.map string
|> String.concat ""
最后,实际上还有更短的方法可以做到这一点,但它是如此可笑的明显,感觉就像在作弊。
问题是,因为字符串是序列,所以它们拥有所有的序列操作是有意义的。你猜怎么着?他们是这样!具体来说,有一个函数String.map
,它与 执行相同的操作Seq.map
,但用于字符串:
let result = String.map System.Char.ToUpper s