将不在列表中的两个字符连接成字符串的高性能,惯用方法

Dan*_*iel 0 f#

我在C#中完成了大部分开发工作,我只是在学习F#.这是我想用C#做的事情:

string AddChars(char char1, char char2) => char1.ToString() + char2.ToString();
Run Code Online (Sandbox Code Playgroud)

编辑:添加ToString()方法到C#示例.

我想在F#中编写相同的方法,除了这个我不知道怎么做:

let addChars char1 char2 = Char.ToString(char1) + Char.ToString(char2)
Run Code Online (Sandbox Code Playgroud)

有没有办法将这些chars 连接到一个string没有先转换成strings的?


边注:

我也考虑过制作一个char阵列并将其转换为a string,但这看起来同样浪费.

let addChars (char1:char) (char2: char) = string([|char1; char2|])
Run Code Online (Sandbox Code Playgroud)

Abi*_*n47 7

正如我在评论中所说,你的C#代码不会做你想要的(即将字符连接成一个字符串).在C#中,添加a char和a char将导致int.这样做的原因是因为char类型没有定义+运算符,所以C#恢复到最接近的可比较类型,这恰好是int.(来源)

因此,要完成此行为,您需要执行与您在F#中已尝试执行的操作类似的操作:

char a = 'a';
char b = 'b';

// This is the wrong way to concatenate chars, because the
// chars will be treated as ints and the result will be 195.
Console.WriteLine(a + b);                              

// These are the correct ways to concatenate characters into
// a single string. The result of all of these will be "ab".
// The third way is the recommended way as it is concise and 
// involves creating the fewest temporary objects.
Console.WriteLine(a.ToString() + b.ToString());        
Console.WriteLine(Char.ToString(a) + Char.ToString(b));
Console.WriteLine(new String(new[] { a, b }));
Run Code Online (Sandbox Code Playgroud)

(见https://dotnetfiddle.net/aEh1FI)

F#与连接两个或多个chars不会导致a的方式相同String.与C#不同,它在另一个结果中产生char,但过程是相同的 - 将char值视为int并加在一起,结果是char总和的表示.

实际上,在F#中连接chars 的方法String就是你已经拥有的,并且是C#等价物的直接翻译:

let a = 'a'
let b = 'b'

// This is still the wrong way (prints 'Ã')
printfn "%O" (a + b)

// These are still the right ways (prints "ab")
printfn "%O" (a.ToString() + b.ToString())
printfn "%O" (Char.ToString(a) + Char.ToString(b))
printfn "%O" (String [| a;b |]) // This is still the best way
Run Code Online (Sandbox Code Playgroud)

(见https://dotnetfiddle.net/ALwI3V)

"来自char数组的字符串"方法的最佳方法是双重的.首先,它是最简洁的,因为您可以看到该方法提供了两种语言中最短的代码行(并且差异只会随着您添加越来越多的chars 而增加).第二,在最终结果之前只创建了一个临时对象(数组)String,而另外两个方法涉及将两个单独的临时String对象提供给最终结果.

(另外,我不确定它是否以这种方式工作,因为String构造函数隐藏在外部源中,但我想传入构造函数的数组将被用作String后台数据,因此它不会最终获得浪费了.) String s是不可变的,但是直接使用传递的数组作为创建String的后备数据可能会导致数组的引用可以保存在程序的其他地方并危及其String不变性,因此这种推测在实践中不会飞.(来源:@CaringDev)


您可以在F#中做的另一个可能更惯用的选项是使用该sprintf函数来组合两个字符(Credit:@rmunn):

let a = 'a'
let b = 'b'
let s = sprintf "%c%c" a b

printfn "%O" s 

// Prints "ab"
Run Code Online (Sandbox Code Playgroud)

(见https://dotnetfiddle.net/Pp9Tee)

然而,关于这种方法的警告的注意事项是它几乎肯定会比上面列出的其他三种方法慢得多.这是因为不是String直接处理数组或数据,sprintf而是在输出上执行更高级的格式化逻辑.(我现在不能自己对此进行基准测试,但是下面插入@ TomasPetricek的benckmarking代码,如果你的性能达到10倍或更高,我也不会感到惊讶.)

这可能不是什么大问题,因为单个转换它仍然会比任何最终用户可能注意到的快得多,但是如果要在任何性能关键代码中使用它,请小心.