C#与F#的默认排序

Law*_*nce 18 c# sorting string f# collation

考虑代码两个片段,简单地订购串C#F#分别为:

C#:

var strings = new[] { "Tea and Coffee", "Telephone", "TV" };
var orderedStrings = strings.OrderBy(s => s).ToArray();
Run Code Online (Sandbox Code Playgroud)

F#:

let strings = [| "Tea and Coffee"; "Telephone"; "TV" |]
let orderedStrings =
    strings
    |> Seq.sortBy (fun s -> s)
    |> Seq.toArray
Run Code Online (Sandbox Code Playgroud)

这两个代码片段返回不同的结果:

  • C#:茶和咖啡,电话,电视
  • F#:电视,茶和咖啡,电话

在我的具体情况下,我需要关联这两种语言之间的排序逻辑(一种是生产代码,一种是测试断言的一部分).这提出了几个问题:

  • 订购逻辑存在差异的根本原因是什么?
  • 在我的情况下,克服这个"问题"的推荐方法是什么?
  • 这种现象是否特定于字符串,还是也适用于其他.NET类型?

编辑

在回答几个探测性评论时,运行下面的片段可以更多地了解这种排序差异的确切性质:

F#:

let strings = [| "UV"; "Uv"; "uV"; "uv"; "Tv"; "TV"; "tv"; "tV" |]
let orderedStrings =
    strings
    |> Seq.sortBy (fun s -> s)
    |> Seq.toArray
Run Code Online (Sandbox Code Playgroud)

C#:

var strings = new[] { "UV", "Uv", "uv", "uV", "TV", "tV", "Tv", "tv" };
var orderedStrings = strings.OrderBy(s => s).ToArray();
Run Code Online (Sandbox Code Playgroud)

得到:

  • C#:tv,tV,Tv,TV,uv,uV,Uv,UV
  • F#:电视,电视,紫外线,紫外线,电视,电视,紫外线,紫外线

字符串的字典顺序不同,因为字符的基本顺序不同:

  • C#:"aAbBcCdD ... tTuUvV ......"
  • F#:"ABC..TUV..Zabc..tuv .."

Ric*_*ard 6

不同的库对字符串进行默认比较操作的不同选择.F#严格默认为区分大小写,而LINQ to Objects不区分大小写.

双方List.sortWithArray.sortWith允许指定的比较.就像过载一样Enumerable.OrderBy.

但是,该Seq模块似乎没有等效物(并且在4.6中没有添加一个).

针对具体问题:

订购逻辑存在差异的根本原因是什么?

两个订单都有效.在英语中,不敏感似乎更自然,因为这就是我们习惯的.但这并没有使它更正确.

在我的情况下,克服这个"问题"的推荐方法是什么?

明确这种比较.

这种现象是否特定于字符串,还是也适用于其他.NET类型?

char也会受到影响.以及有多种可能排序的任何其他类型(例如,People类型:您可以根据具体要求按名称或出生日期订购).


lat*_*kin 6

请参阅语言规范的第8.15.6节.

字符串,数组和本机整数具有特殊的比较语义,IComparable如果实现的话,其他所有内容都会发生(模数会产生相同结果的各种优化).

特别是,F#字符串默认使用序数比较,而大多数.NET默认使用文化感知比较.

这显然是F#和其他.NET语言之间令人困惑的不兼容性,但它确实有一些好处:

  • OCAML compat
  • 字符串和字符比较是一致的
    • C# Comparer<string>.Default.Compare("a", "A") // -1
    • C# Comparer<char>.Default.Compare('a', 'A') // 32
    • F# compare "a" "A" // 1
    • F# compare 'a' 'A' // 32

编辑:

请注意,说明"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
Run Code Online (Sandbox Code Playgroud)