strings - string literals c#
Ordenamiento por defecto en C#vs. F# (4)
Consulte la sección 8.15.6 de la especificación de idioma .
Las cadenas, los arreglos y los enteros nativos tienen una semántica de comparación especial, todo lo demás solo es IComparable
si se implementa (módulo varias optimizaciones que producen el mismo resultado).
En particular, las cadenas F # usan la comparación ordinal de forma predeterminada, en contraste con la mayoría de .NET que usa la comparación basada en la cultura de forma predeterminada.
Esto es obviamente una incompatibilidad confusa entre F # y otros lenguajes .NET, sin embargo tiene algunos beneficios:
- Compatibilidad OCAML
- Las comparaciones de cadenas y caracteres son consistentes.
- 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
- C #
Editar:
Tenga en cuenta que es engañoso (aunque no incorrecto) afirmar que "F # usa comparación de cadenas que distinguen mayúsculas y minúsculas ". F # usa la comparación ordinal , que es más estricta que solo distingue entre mayúsculas y minúsculas.
// 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
Considere los dos fragmentos de código que simplemente ordenan cadenas en C#
y F#
respectivamente:
DO#:
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
Estos dos fragmentos de código devuelven resultados diferentes:
- C #: Té y Café, Teléfono, TV
- F #: TV, té y café, teléfono
En mi caso específico, necesito correlacionar la lógica de ordenamiento entre estos dos idiomas (uno es el código de producción y el otro es parte de una afirmación de prueba). Esto plantea algunas preguntas:
- ¿Hay una razón subyacente para las diferencias en la lógica de orden?
- ¿Cuál es la forma recomendada de superar este "problema" en mi situación?
- ¿Este fenómeno es específico de las cadenas o se aplica también a otros tipos de .NET?
EDITAR
En respuesta a varios comentarios de sondeo, la ejecución de los fragmentos a continuación revela más sobre la naturaleza exacta de las diferencias de este ordenamiento:
F#:
let strings = [| "UV"; "Uv"; "uV"; "uv"; "Tv"; "TV"; "tv"; "tV" |]
let orderedStrings =
strings
|> Seq.sortBy (fun s -> s)
|> Seq.toArray
DO#:
var strings = new[] { "UV", "Uv", "uv", "uV", "TV", "tV", "Tv", "tv" };
var orderedStrings = strings.OrderBy(s => s).ToArray();
Da:
- C #: TV, TV, TV, TV, UV, UV, UV, UV
- F #: TV, TV, UV, Uv, TV, TV, UV, UV
El orden lexicográfico de las cadenas difiere debido a una diferencia en el orden subyacente de los caracteres:
- C #: "aAbBcCdD ... tTuUvV ..."
- F #: "ABC..TUV..Zabc..tuv .."
Diferentes bibliotecas hacen diferentes elecciones de la operación de comparación predeterminada en cadenas. F # es el incumplimiento estricto de la sensibilidad a mayúsculas, mientras que LINQ to Objects no distingue entre mayúsculas y minúsculas.
Tanto List.sortWith
como Array.sortWith
permiten Array.sortWith
la comparación. Al igual que una sobrecarga de Enumerable.OrderBy
.
Sin embargo, el módulo Seq
no parece tener un equivalente (y uno no se agrega en 4.6).
Para las preguntas específicas:
¿Hay una razón subyacente para las diferencias en la lógica de orden?
Ambos pedidos son válidos. En los casos de inglés, la insensibilidad parece más natural, porque eso es a lo que estamos acostumbrados. Pero esto no lo hace más correcto.
¿Cuál es la forma recomendada de superar este "problema" en mi situación?
Sea explícito sobre el tipo de comparación.
¿Este fenómeno es específico de las cadenas o se aplica también a otros tipos de .NET?
char
también se verá afectado. Y cualquier otro tipo donde haya más de un pedido posible (por ejemplo, un tipo de People
: puede ordenar por nombre o fecha de nacimiento según los requisitos específicos).
Esto no tiene nada que ver con C # vs F #, o incluso con IComparable
, pero se debe a las diferentes implementaciones de ordenación en las bibliotecas.
El TL; DR; La versión es que las cadenas de clasificación pueden dar diferentes resultados:
"tv" < "TV" // false
"tv".CompareTo("TV") // -1 => implies "tv" *is* smaller than "TV"
O incluso más claro:
"a" < "A" // false
"a".CompareTo("A") // -1 => implies "a" is smaller than "A"
Esto se debe a que CompareTo
utiliza la cultura actual (consulte MSDN) .
Podemos ver cómo esto se lleva a cabo en la práctica con algunos ejemplos diferentes.
Si usamos la clasificación estándar de F # obtenemos el primer resultado en mayúscula:
let strings = [ "UV"; "Uv"; "uV"; "uv"; "Tv"; "TV"; "tv"; "tV" ]
strings |> List.sort
// ["TV"; "Tv"; "UV"; "Uv"; "tV"; "tv"; "uV"; "uv"]
Incluso si lanzamos a IComparable
obtenemos el mismo resultado:
strings |> Seq.cast<IComparable> |> Seq.sort |> Seq.toList
// ["TV"; "Tv"; "UV"; "Uv"; "tV"; "tv"; "uV"; "uv"]
Por otro lado, si usamos Linq de F #, obtenemos el mismo resultado que el código de C #:
open System.Linq
strings.OrderBy(fun s -> s).ToArray()
// [|"tv"; "tV"; "Tv"; "TV"; "uv"; "uV"; "Uv"; "UV"|]
De acuerdo con MSDN , el método OrderBy
"compara las claves utilizando el comparador predeterminado Predeterminado".
Las bibliotecas de F # no usan Comparer
de forma predeterminada, pero podemos usar sortWith
:
open System.Collections.Generic
let comparer = Comparer<string>.Default
Ahora, cuando hacemos este tipo, obtenemos el mismo resultado que LINQ OrderBy
:
strings |> List.sortWith (fun x y -> comparer.Compare(x,y))
// ["tv"; "tV"; "Tv"; "TV"; "uv"; "uV"; "Uv"; "UV"]
Alternativamente, podemos usar la función integrada CompareTo
, que da el mismo resultado:
strings |> List.sortWith (fun x y -> x.CompareTo(y))
// ["tv"; "tV"; "Tv"; "TV"; "uv"; "uV"; "Uv"; "UV"]
Moraleja del cuento: si le importa la clasificación, ¡siempre especifique la comparación específica a utilizar!
Gracias a @Richard y sus respuestas por indicarme la dirección para avanzar un poco más en la comprensión de este problema
Mis problemas parecen haberse arraigado en no entender completamente las consecuencias de la restricción de comparison
en F #. Aquí está la firma de Seq.sortBy
Seq.sortBy : (''T -> ''Key) -> seq<''T> -> seq<''T> (requires comparison)
Mi suposición era que si el tipo ''T
implementaba IComparable
, esto se usaría en la clasificación. Debería haber consultado esta pregunta primero: comparación de F # frente a C # IComparable , que contiene algunas referencias útiles, pero que requiere una lectura más cuidadosa para apreciar completamente lo que está sucediendo.
Entonces, para intentar responder a mis propias preguntas:
¿Hay una razón subyacente para las diferencias en la lógica de orden?
Sí. La versión de C # parece utilizar la implementación de IComparable
de la cadena, mientras que la versión de F # no lo hace.
¿Cuál es la forma recomendada de superar este "problema" en mi situación?
Aunque no puedo comentar si esto es "recomendado", el siguiente order
función F # usará una implementación de IComparable
si hay una en el tipo relevante:
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
¿Este fenómeno es específico de las cadenas o se aplica también a otros tipos de .NET?
Claramente hay algunas sutilezas involucradas con la relación entre la restricción de comparison
y la interfaz de IComparable
. Para estar seguro, seguiré los consejos de @ Richard y siempre seré explícito sobre el tipo de comparación, probablemente usando la función anterior para "priorizar" usando IComparable
en la clasificación.