现在我正在使用这样的方法:
let x_rev = new string(x.Reverse().ToArray())
答案 0 :(得分:9)
以下是一些基于Timwi对Nate答案的评论的代码。屏幕上显示单个逻辑字母(由屏幕上显示),由多个实际字符组成。颠倒字符的顺序会使这些字母变成乱码。
Timwi帮助指出框架提供了TextElementEnumerator,它在逻辑文本元素而不是字符方面起作用,并正确处理这些多字符字母。我之前没有听说过这个类,所以我编写了一些代码,它使用TextElementEnumerator正确地反转字符串,并将结果与天真的字符串反转进行比较。
open System
open System.Globalization
// five characters that render as three letters: "àÆ"
let myString = "\uD800\uDC00\u0061\u0300\u00C6"
// naive reversal scrambles the string: "Æ̀a��"
// (note that while this produces the wrong results,
// it probably is faster on large strings than using
// LINQ extension methods, which are also wrong)
let naive = String(myString.ToCharArray() |> Array.rev)
// use a TextElementEnumerator to create a seq<string>
// that handles multi-character text elements properly
let elements =
seq {
let tee = StringInfo.GetTextElementEnumerator(myString)
while tee.MoveNext() do
yield tee.GetTextElement()
}
// properly reversed: "Æà"
let reversed = elements |> Array.ofSeq |> Array.rev |> String.concat ""
答案 1 :(得分:5)
我的回答是基于@Joel的回答,而回答是基于@Timwi的回答。我将它呈现为最美丽和最简单的正确的答案,尽管肯定不是最佳表现(使用+
的折叠会导致这种情况;但主要的美化改进是使用ParseCombiningCharacters
和GetNextTextElement
代替那次健全性测试TextElementEnumerator
。将Reverse
添加为String
的扩展也很不错:
open System
open System.Globalization
type String with
member self.Reverse() =
StringInfo.ParseCombiningCharacters(self)
|> Seq.map (fun i -> StringInfo.GetNextTextElement(self, i))
|> Seq.fold (fun acc s -> s + acc ) ""
用法:
> "\uD800\uDC00\u0061\u0300\u00C6".Reverse();;
val it : string = "Æà"
修改强>
我在乘坐汽车之家的同时也想到了这种新颖的变化,因为我们使用String.concat
可能会表现得更好。类型扩展名被省略:
let rev str =
StringInfo.ParseCombiningCharacters(str)
|> Array.rev
|> Seq.map (fun i -> StringInfo.GetNextTextElement(str, i))
|> String.concat ""
编辑(目前为止的最佳解决方案):
此解决方案使用另一种StringInfo
方法来枚举文本元素,这又避免了使用令人不快的方法来处理TextElementEnumerator
但不会导致对内部StringInfo.GetCurrentTextElementLen
的调用次数增加一倍像以前的解决方案。我这次也使用了就地阵列反转,这导致了显着的性能提升。
let rev str =
let si = StringInfo(str)
let teArr = Array.init si.LengthInTextElements (fun i -> si.SubstringByTextElements(i,1))
Array.Reverse(teArr) //in-place reversal better performance than Array.rev
String.Join("", teArr)
上述解决方案基本上等同于以下内容(我希望我们可以提高性能,但我可以测量没有显着差异):
let rev str =
let ccIndices = StringInfo.ParseCombiningCharacters(str)
let teArr =
Array.init
ccIndices.Length
(fun i ->
if i = ccIndices.Length-1 then
str.Substring(i)
else
let startIndex = ccIndices.[i]
str.Substring(startIndex, ccIndices.[i+1] - startIndex))
Array.Reverse(teArr) //in-place reversal better performance than Array.rev
String.Join("", teArr)
答案 2 :(得分:4)
我无法相信这里没有人为此提供通用的解决方案!
Generic reverse with O(n) runtime.
然后,只需使用:
let rec revAcc xs acc =
match xs with
| [] -> acc
| h::t -> revAcc t (h::acc)
let rev xs =
match xs with
| [] -> xs
| [_] -> xs
| h1::h2::t -> revAcc t [h2;h1]
let newValues =
values
|> Seq.toList
|> rev
|> List.toSeq
newValues
这就是F#的全部意义!
答案 3 :(得分:2)
如果你正在做的是来自Enumerable.Reverse()的MSDN,那么你可能得到了最简单的解决方案。
如果您没有使用.NET 3.5(读取LINQ(不知道F#是否在此之前就已存在)),您可以使用Array.Reverse()方法,但是,生成的代码非常相似。 / p>
我只想说,你所拥有的是我能用来反转字符串的最优雅方式,我多次使用Enumerable.Reverse()
来反转项目中字符串的顺序。显然,如果String构造函数采用IEnumerable<Char>
,我们可以跳过.ToArray()
位,这在我看来会使代码更好一些,但是就目前而言,额外的.ToArray()
不是一切都那么糟糕。
如果您真的想要,可以在C#中编写扩展方法,并在F#项目中添加对该库的引用,C#扩展方法如下所示:
public static String ReverseString(this String toReverse)
{
return new String(toReverse.Reverse().ToArray());
}
这增加了一个额外的依赖,只有真正的好处是让你的F#代码更简单一些,如果你在整个地方反转字符串,它可能是值得的,否则,我只是把你的东西包起来'我们采用了正常的F#方法并以这种方式使用它。
虽然,比我更聪明的人可能有更美好的方式去做。
答案 4 :(得分:1)
结合以前最好的答案,稍加更新:
module String =
open System.Globalization
let rev s =
seq {
let rator = StringInfo.GetTextElementEnumerator(s)
while rator.MoveNext() do
yield rator.GetTextElement()
}
|> Array.ofSeq
|> Array.rev
|> String.concat ""
String.rev "\uD800\uDC00\u0061\u0300\u00C6"