考虑一下分别在C#
和F#
中对字符串进行排序的两段代码:
C#:
var strings = new[] { "Tea and Coffee", "Telephone", "TV" };
var orderedStrings = strings.OrderBy(s => s).ToArray();
F#:
let strings = [| "Tea and Coffee"; "Telephone"; "TV" |]
let orderedStrings =
strings
|> Seq.sortBy (fun s -> s)
|> Seq.toArray
这两段代码返回不同的结果:
在我的特定案例中,我需要将这两种语言之间的排序逻辑关联起来(一种是产品代码,另一种是测试断言的一部分)。这提出了几个问题:
编辑
为了回应几个试探性的评论,运行下面的片段揭示了更多关于这种排序差异的确切性质:
F#:
let strings = [| "UV"; "Uv"; "uV"; "uv"; "Tv"; "TV"; "tv"; "tV" |]
let orderedStrings =
strings
|> Seq.sortBy (fun s -> s)
|> Seq.toArray
C#:
var strings = new[] { "UV", "Uv", "uv", "uV", "TV", "tV", "Tv", "tv" };
var orderedStrings = strings.OrderBy(s => s).ToArray();
提供:
字符串的字典顺序因字符的基本顺序不同而有所不同:
"aAbBcCdD...tTuUvV..."
:
发布于 2015-06-23 21:55:12
请参阅language spec的8.15.6节。
字符串、数组和本机整数都有特殊的比较语义,如果实现了IComparable
(对各种优化进行取模,会产生相同的结果),那么其他所有的东西都会被用来比较。
特别是,与大多数默认使用区域性比较的.NET不同,F#字符串在默认情况下使用序号比较。
这显然是F#和其他.NET语言之间令人困惑的不兼容性,但它确实有一些好处:
编辑:
注意,声明"F#使用区分大小写的字符串比较“是有误导性的(虽然不是不正确的)。F#使用序号比较,这比区分大小写更严格。
// case-sensitive comparison
StringComparer.InvariantCulture.Compare("[", "A") // -1
StringComparer.InvariantCulture.Compare("[", "a") // -1
// ordinal comparison
// (recall, '[' lands between upper- and lower-case chars in the ASCII table)
compare "[" "A" // 26
compare "[" "a" // -6
发布于 2015-06-23 09:49:58
不同的库对字符串进行不同的默认比较操作。F#严格默认区分大小写,而LINQ不区分大小写。
List.sortWith
和Array.sortWith
都允许指定比较。Enumerable.OrderBy
的过载也是如此。
然而,Seq
模块似乎没有对应的模块(并且在4.6中没有添加一个模块)。
具体问题:
排序逻辑上的差异有没有潜在的原因?
两种排序都是有效的。在英语中,不敏感似乎更自然,因为这是我们习惯的。但这并不能让它变得更正确。
在我的情况下,解决这个“问题”的推荐方法是什么?
明确说明这种比较。
是特定于字符串的现象,还是它也适用于其他.NET类型?
char
也会受到影响。以及存在多于一种可能排序的任何其他类型(例如,A People
类型:您可以根据具体要求按姓名或出生日期排序)。
发布于 2015-06-23 17:04:24
这与C#与F#,甚至与IComparable
无关,而只是由于库中不同的排序实现。
TL;DR;版本是对字符串进行排序可以得到不同的结果:
"tv" < "TV" // false
"tv".CompareTo("TV") // -1 => implies "tv" *is* smaller than "TV"
或者更清楚的是:
"a" < "A" // false
"a".CompareTo("A") // -1 => implies "a" is smaller than "A"
这是因为CompareTo
使用当前的区域性(see MSDN)。
我们可以通过一些不同的例子来看看这是如何在实践中发挥作用的。
如果我们使用标准的F#排序,我们会得到大写优先的结果:
let strings = [ "UV"; "Uv"; "uV"; "uv"; "Tv"; "TV"; "tv"; "tV" ]
strings |> List.sort
// ["TV"; "Tv"; "UV"; "Uv"; "tV"; "tv"; "uV"; "uv"]
即使我们对IComparable
进行强制转换,也会得到相同的结果:
strings |> Seq.cast<IComparable> |> Seq.sort |> Seq.toList
// ["TV"; "Tv"; "UV"; "Uv"; "tV"; "tv"; "uV"; "uv"]
另一方面,如果我们从F#使用Linq,我们会得到与C#代码相同的结果:
open System.Linq
strings.OrderBy(fun s -> s).ToArray()
// [|"tv"; "tV"; "Tv"; "TV"; "uv"; "uV"; "Uv"; "UV"|]
根据MSDN的说法,OrderBy
方法“使用缺省的比较器缺省值来比较键”。
默认情况下,F#库不使用Comparer
,但我们可以使用sortWith
open System.Collections.Generic
let comparer = Comparer<string>.Default
现在,当我们进行这种排序时,我们得到了与LINQ OrderBy
相同的结果
strings |> List.sortWith (fun x y -> comparer.Compare(x,y))
// ["tv"; "tV"; "Tv"; "TV"; "uv"; "uV"; "Uv"; "UV"]
或者,我们可以使用内置的CompareTo
函数,它会产生相同的结果:
strings |> List.sortWith (fun x y -> x.CompareTo(y))
// ["tv"; "tV"; "Tv"; "TV"; "uv"; "uV"; "Uv"; "UV"]
这个故事的寓意:如果你关心排序,总是指定要使用的具体比较!
https://stackoverflow.com/questions/30999018
复制相似问题