Подтвердить что ты не робот

Сравнение переопределения для набора F #

Есть ли способ переопределить функцию сравнения в наборе F #?

Я не вижу никаких функций построения, которые выполняют функцию IComparer<T> или сравнения:

  • Set.ofSeq и другие не выполняют функцию сравнения Конструктор
  • FSharpSet(IComparer<T> comparer, SetTree<T> tree) является внутренним, потому что
  • SetTree является внутренним и
  • SetTreeModule.ofSeq<a>(IComparer<a> comparer, IEnumerable<a> c) также является внутренним.

Моя фактическая проблема заключается в том, что у меня есть набор ('a * 'a), и мне нужно сравнение, например, (1,3) = (3,1).

Я знаю, что могу обернуть это в тип, реализующий IComparable<T>, но есть ли способ избежать этого?

4b9b3361

Ответ 1

Я думаю, что тип set, доступный в основных библиотеках F #, не позволяет указать ваше собственное сравнение. Однако в PowerPack есть тегированная версия типа, которая позволяет это. Ниже показано, как создать набор с помощью сравнения, который сравнивает целые числа, используя modulo 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)

Ответ 2

Вам следует использовать тип "обертка", который вы предлагаете.

(Для неизменяемых типов, таких как F # Set и Map, существуют проблемы API с настраиваемыми компараторами. Поскольку каждая операция возвращает, например, новый объект Set, новому объекту необходимо предоставить общий доступ к сопоставлению (возможно, проблемы), и нет хорошего способа решить, какой компаратор должен использовать результат, например, результат Set.union двух наборов с разными сопоставителями. Таким образом, Set и Map избегают путаницы здесь, всегда используя сравнение непосредственно на тип элемента. Как упоминает Tomas, PowerPack имеет альтернативный API, который создает часть сравнения этого типа, которая позволяет, например, использовать метод typeafe/compare-safe Union.)

Ответ 3

У меня была аналогичная проблема. Мне также было запрещено использовать только стандартные библиотеки и внешние зависимости. В то же время я хотел использовать существующие реализации интерфейсов IEqualityComparer и IComparer, которые у меня уже были в наличии.

Итак, я закончил создание собственной структуры-оболочки, которую теперь я могу использовать с встроенной картой F # и Set:

[<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

Способ, которым я использую это, - создать оболочку встроенной коллекции и внутренне создать оболочку ключа/элемента, чтобы сделать код пользователя более дружественным. Поэтому у меня было бы что-то вроде

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