Реализация пользовательского сравнения с CustomComparison и CustomEquality в F# кортеже

Я здесь, чтобы задать конкретную тему - я действительно нашел мало информации об этом в Интернете. Я реализую F# версию алгоритма Minimax. Сейчас у меня проблема в том, что я хочу сравнить Leaf моего дерева (структура данных ниже). В поисках ошибок, которые дала мне VS, я пришел к чему-то вроде этого:

Тип дерева, который у меня был:

type TreeOfPosition =
    | LeafP   of Position
    | BranchP of Position * TreeOfPosition list

и соблазн для реализации IComparable

type staticValue = int
[<CustomEquality;CustomComparison>]
type TreeOfPosition =
    | LeafP   of Position * staticValue
    | BranchP of Position * TreeOfPosition list

    override x.Equals(yobj) = 
        match yobj with
        | :? TreeOfPosition as y -> (x = y)
        | _ -> false

    override x.GetHashCode() = hash (x)
    interface System.IComparable with
        member x.CompareTo yobj =
            match yobj with
            | :? TreeOfPosition as y -> compare (x) (y)
            | _ -> invalidArg "yobj" "cannot compare value of different types"

В конце, я просто хочу получить максимум (и минимум) списка LeafP по его статическому значению (вычислить в другой функции).

Код выше компилируется. Однако тестирование с этим:

let p = new Position()
p.Add(1,BLACK)
let a = LeafP(p,1)
let b = LeafP(p,2)

let biger = compare a b
printf "%d" biger

Я получил System.StackruException в строке "|:? TreeOfPosition как y -> compare (x) (y)" в переопределении GetHashCode.

У меня есть тема в hubfs.net ( http://cs.hubfs.net/forums/thread/15891.aspx), где я обсуждаю свой минимакс. Здесь вы можете найти мой последний код ( http://www.inf.ufrgs.br/~pmdusso/works/Functional_Implementation_Minimax_FSharp.htm)

Заранее спасибо,

Педро Дуссо

Ну, я очень ясно понял идею, но не могу заставить ее работать. Помня, что я хочу получить лист с максимальным статическим значением из списка листьев ("List.max":P), я думаю, что реализация CompareTo или же Equals пусть List.max работает на них, правильно? Я сочиняю такие вещи:

let mycompare x y = 
  match x, y with
  // Compare values stored as part of your type
  | LeafP(_, n1), LeafP(_, n2) -> compare n1 n2
  //| BranchP(_, l1), BranchP(_, l2) -> compare l1 l2 //I do not need Branch lists comparison
  | _ -> 0 // or 1 depending on which is list...

[< CustomEquality;CustomComparison >]
type TreeOfPosition =
    | LeafP   of Position * int
    | BranchP of Position * TreeOfPosition list

    override x.Equals(yobj) = 
       match yobj with
       | :? TreeOfPosition as y -> (x = y)
       | _ -> false

    override x.GetHashCode() = hash (x)
    interface System.IComparable with
       member x.CompareTo yobj = 
          match yobj with 
          | :? TreeOfPosition as y -> mycompare x y
          | _ -> invalidArg "yobj" "cannot compare value of different types" 

Проблемы, которые у меня возникают при организации функций таким образом:

1) Дискриминатор шаблона 'LeafP' не определен (с красной линией LeafP)

2) (77,39): ошибка FS0039: значение или конструктор 'mycompare' не определены, когда я пытаюсь выполнить команду ALT ENTER, это сообщение появляется в моем F# Interactive. Позиция {77,39} соответствует началу вызова mycompare (в GetHashCode).

Что я делаю не так? Что я могу сделать лучше?

Спасибо большое,

Педро Дуссо

РЕДАКТИРОВАТЬ 3 - Решено

Да! Я управляю твоим ответом на работу окончательно!

Окончательный код здесь:

[<CustomEquality;CustomComparison>]
type TreeOfPosition =
    | LeafP   of Position * int
    | BranchP of Position * TreeOfPosition list

    //Func: compare
    //Retu: -1: first parameter is less than the second
    //       0: first parameter is equal to the second
    //       1: first parameter is greater than the second
    static member mycompare (x, y) = 
        match x, y with
        // Compare values stored as part of your type
        | LeafP(_, n1), LeafP(_, n2) -> compare n1 n2
        | _ -> 0 // or 1 depending on which is list...

    override x.Equals(yobj) = 
        match yobj with
        | :? TreeOfPosition as y -> (x = y)
        | _ -> false

    override x.GetHashCode() = hash (x)
    interface System.IComparable with
       member x.CompareTo yobj = 
          match yobj with 
          | :? TreeOfPosition as y -> TreeOfPosition.mycompare(x, y)
          | _ -> invalidArg "yobj" "cannot compare value of different types" 

Спасибо за ответ!

Педро Дуссо

1 ответ

Решение

Прежде всего, вы получаете исключение, потому что compare функция вызывает CompareTo метод значений, которые вы сравниваете (то есть x.ComaperTo(y)). Значения, которые вы сравниваете, используя compare в пользовательской реализации CompareTo это значения, которые вас просят сравнить (по времени выполнения), поэтому это вызывает переполнение стека.

Обычный способ реализации CompareTo или же Equals это сравнить только некоторые значения, которые вы храните в вашем типе. Например, вы можете написать что-то вроде этого:

РЕДАКТИРОВАТЬ: Вы можете написать вспомогательную функцию mycopare сделать сравнение (или вы могли бы просто изменить CompareTo реализация). Однако, если вы хотите использовать функцию, вам нужно переместить ее в объявление типа (чтобы она знала о типе - обратите внимание, что в F# порядок объявления имеет значение!)

Один из способов написать это так:

[<CustomEquality; CustomComparison >] 
type TreeOfPosition = 
  | LeafP   of Position * int 
  | BranchP of Position * TreeOfPosition list 

  override x.Equals(yobj) =  
     match yobj with 
     | :? TreeOfPosition as y -> 
        // TODO: Check whether both y and x are leafs/branches
        // and compare their content (not them directly)
     | _ -> false 
  override x.GetHashCode() = // TODO: hash values stored in leaf/branch

  interface System.IComparable with 
     member x.CompareTo yobj =  

       // Declare helper function inside the 'CompareTo' member
       let mycompare x y = 
         match x, y with
         // Compare values stored as part of your type
         | LeafP(_, n1), LeafP(_, n2) -> compare n1 n2
         | BranchP(_, l1), BranchP(_, l2) -> compare l1 l2
         | _ -> -1 // or 1 depending on which is list...

       // Actual implementation of the member
       match yobj with 
       | :? TreeOfPosition as y -> mycompare x y
       | _ -> invalidArg "yobj" "cannot compare value of different types" 

Это будет работать, потому что каждый звонок compare занимает только некоторую часть данных, так что вы делаете некоторые успехи.

Другие вопросы по тегам