2010-04-03 10 views
12

¿Hay alguna manera de anular la función de comparación en un conjunto F #?comparación Invalidar F # coloca

no veo ninguna función de conjunto de la construcción que tienen una función IComparer<T> o comparación:

  • Set.ofSeq et al no toman una función de comparación
  • FSharpSet(IComparer<T> comparer, SetTree<T> tree) constructor es interno, porque
  • SetTree es interno y
  • SetTreeModule.ofSeq<a>(IComparer<a> comparer, IEnumerable<a> c) es obviamente interno también.

Mi problema actual es que tengo un conjunto de ('a * 'a) y quiero una comparación tal que, por ejemplo, (1,3) = (3,1).

Sé que podría terminar con esto en un tipo de implementación IComparable<T>, pero ¿hay alguna manera de evitar esto?

+0

Acabo de volver a esta pregunta: es una idea terrible. Crea un nuevo tipo que represente el comportamiento que deseas. –

Respuesta

17

creo que el tipo set disponibles en las bibliotecas centrales F # no permite especificar su propia comparación. Sin embargo, hay una versión etiquetada del tipo en PowerPack que permite esto. A continuación se muestra cómo crear un conjunto con el comparador que compara números enteros utilizando módulo 10:

#r @"FSharp.PowerPack.dll" 
open System.Collections.Generic 
open Microsoft.FSharp.Collections.Tagged 

type MyComparer() = 
    interface IComparer<int> with 
    member x.Compare(a, b) = (a % 10).CompareTo(b % 10) 

// Type alias for a set that uses 'MyComparer' 
type MySet = Tagged.Set<int, MyComparer> 

let comparer = new MyComparer() 
let s1 = MySet.Create(comparer, [1; 2; 3]) 
let s2 = MySet.Create(comparer, [11; 14]) 

MySet.Union(s1, s2) 
4

Es necesario utilizar un tipo de "contenedor" como usted sugiere.

(Para los tipos inmutables como F # Set y Map, hay problemas de la API con comparadores personalizados. Desde cada ficha de funcionamiento, es decir un nuevo Set objeto, el nuevo objeto tiene que compartir el comparador (que puede dar lugar a enhebrar cuestiones), y hay hay una buena manera de decidir qué Comparer el resultado debe utilizar para, por ejemplo el resultado de Set.union de dos conjuntos con diferentes comparadores. Así Set y Map evitar la confusión aquí usando siempre la comparación directa del tipo de elemento. Como se menciona Tomás, el PowerPack tiene una API alternativa que hace que la parte comparador del tipo, que permite a un typesafe/comparar-safe Union método, por ejemplo.)

0

Tuve un problema similar. También estaba restringido para usar solo las bibliotecas estándar y sin dependencias externas. Al mismo tiempo, quería hacer uso de las implementaciones existentes de las interfaces IEqualityComparer y IComparer que ya tenía a mano.

Así, terminé de hacer mi propia estructura contenedora que ahora puedo usar con el F # 's integrado y conjunto:

[<Struct>] 
[<CustomComparison>] 
[<CustomEquality>] 
type ComparisonAdapter<'T>(value: 'T, comparer: IComparer<'T>, eqComparer: IEqualityComparer<'T>) = 
    new(value) = ComparisonAdapter(value, Comparer<'T>.Default, EqualityComparer<'T>.Default) 
    member this.CompareTo (cmp: IComparer<'T>, v: 'T) = cmp.Compare(v, value) 
    member this.CompareTo (v: 'T) = this.CompareTo(comparer, v) 
    member this.CompareTo (c: ComparisonAdapter<'T>) = c.CompareTo(comparer, value) 
    member this.CompareTo (o: obj) = 
     if (o :? Comparison<'T>) then this.CompareTo(downcast o: ComparisonAdapter<'T>) 
     else if (o :? 'T) then this.CompareTo(downcast o: 'T) 
     else if (o :? IComparable) then ((downcast o: IComparable)).CompareTo(value) 
     else raise (NotSupportedException()) 
    member this.Equals (c: ComparisonAdapter<'T>): bool = c.Equals(eqComparer, value) 
    member this.Equals (cmp: IEqualityComparer<'T>, v: 'T): bool = cmp.Equals(v, value) 
    member this.Equals (v: 'T): bool = eqComparer.Equals(v, value) 
    override this.Equals (o: obj): bool = 
     if (o :? Comparison<'T>) then this.Equals(downcast o: ComparisonAdapter<'T>) 
     else if (o :? 'T) then this.Equals(downcast o: 'T) 
     else false 
    override this.GetHashCode() = eqComparer.GetHashCode value 
    member this.Value with get() = value 
    interface IEquatable<'T> with member this.Equals other = this.Equals(eqComparer, other) 
    interface IComparable<'T> with member this.CompareTo other = this.CompareTo(comparer, other) 
    interface IComparable with member this.CompareTo o = this.CompareTo o 

La forma en que lo uso es para crear un envoltorio de la incorporada en la recopilación y crea internamente el contenedor de clave/elemento para hacer que el código de usuario sea más amigable. Entonces tendría algo así como

type MyCustomMap<'TKey, 'TValue>(cmp: IComparer<'TKey>, eq: IEqualityComparer<'TKey>) = 
    let innerMap = new Map<ComparisonAdapter<'TKey>, 'TValue>() 
    member this Add key value = 
     let actualKey = new ComparisonAdapter<'TKey>(key, cmp, eq) 
     innerMap.Add actualKey value