在C#与F#默认排序

问题描述 投票:18回答:4

考虑代码两个片段,简单地订购分别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

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

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

在我的特定情况下,我需要这两种语言(其中一个是生产代码,一个是测试断言的一部分)之间的次序的逻辑关联。这就提出了几个问题:

  • 是否有在订货逻辑差异的根本原因?
  • 什么是我的情况,以解决这个“问题”是推荐的方式?
  • 这种现象特定字符串,或是否适用于其他.NET类型呢?

编辑

在回答几个探测意见,运行碎片下面揭示更多关于这个排序的差异的确切性质:

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();

得到:

  • C#:电视,电视,淋浴,电视,紫外线,紫外线,紫外线,紫外线
  • F#:电视,电视,紫外线,紫外线,电视,电视,紫外线,紫外线

字符串的词典式排序不同,因为在字符的基本顺序的差异:

  • C#中: “AABBCCDD ... tTuUvV ......”
  • F#: “ABC..TUV..Zabc..tuv ..”
c# string sorting f# collation
4个回答
6
投票

language spec第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

6
投票

不同的库就串默认比较操作的不同选择。 F#是严格默认为区分大小写,而LINQ到对象是不区分大小写的。

无论List.sortWithArray.sortWith允许指定的比较。由于不Enumerable.OrderBy的过载。

然而Seq模块不会出现具有等同的(和一个没有在4.6添加)。

对于具体的问题:

是否有在订货逻辑差异的根本原因?

这两种排序都是有效的。在英语的情况下不敏感似乎更自然,因为这是我们已经习惯了。但是,这并不使之更正确。

什么是我的情况,以解决这个“问题”是推荐的方式?

要明确的那种比较。

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

char也将受到影响。和任何其他类型那里有不止一个可能的排序(如People类型:您可以通过名称或视具体要求出生日期顺序)。


4
投票

这无关用C#VS 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"]

在另一方面,如果我们使用Linq的F#中,我们得到了相同的结果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"] 

道德的故事:如果你关心排序,始终指定具体的比较中使用!


2
投票

由于在了解这个问题上进一步@Richard和his answers指着我的方向开始有点

我的问题似乎已根植在没有完全了解F#中comparison约束的后果。这里是Seq.sortBy的签名

Seq.sortBy : ('T -> 'Key) -> seq<'T> -> seq<'T> (requires comparison)

我的假设是,如果类型'T实现IComparable那么这将在排序中使用。我应该咨询这个问题,第一:F# comparison vs C# IComparable,其中包含了一些有益的参考,但需要进一步的仔细阅读完全理解到底是怎么回事。

所以,试图回答我自己的问题:

是否有在订货逻辑差异的根本原因?

是。 C#的版本似乎使用的字符串的实现IComparable的,而F#版本没有。

什么是我的情况,以解决这个“问题”是推荐的方式?

虽然我不能在这是否是“推荐”发表评论,如果有一个对相关类型的F#功能order下面将使用IComparable的实现:

let strings = [| "UV"; "Uv"; "uV"; "uv"; "Tv"; "TV"; "tv"; "tV" |]
let order<'a when 'a : comparison> (sequence: seq<'a>) = 
    sequence 
    |> Seq.toArray
    |> Array.sortWith (fun t1 t2 ->
        match box t1 with
        | :? System.IComparable as c1 -> c1.CompareTo(t2)
        | _ ->
            match box t2 with
            | :? System.IComparable as c2 -> c2.CompareTo(t1)
            | _ -> compare t1 t2)
let orderedValues = strings |> order

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

显然有参与了comparison约束和IComparable接口之间的关系有些微妙。为了安全起见,我将关注@理查德的意见,并始终明确的那种比较的 - 可能使用上面的函数为“优先”在分拣使用IComparable

© www.soinside.com 2019 - 2024. All rights reserved.