2009-08-26 18 views
6

Dlaczego dozwolone wiązania nie są dozwolone w dyskryminowanej unii? Zakładam, że ma to związek z uruchamianiem wiązań w domyślnym konstruktorze?Dyskryminowana Unia i wiązanie?

Warto zauważyć, że wszelkie sugestie dotyczące przepisywania AI_Choose byłyby mile widziane. Chcę zachować ważony priorytet w krotce z AI. Moim pomysłem jest, aby AI_Weighted_Priority odziedziczyć AI_Priority i zastąpić Wybierz. Nie chcę zajmować się skompresowanie list o różnych długościach (złą praktyką imo.)

open AI 

type Condition = 
    | Closest of float 
    | Min 
    | Max 
    | Average 
    member this.Select (aiListWeight : list<AI * float>) = 
     match this with 
     | Closest(x) -> 
      aiListWeight 
      |> List.minBy (fun (ai, priority) -> abs(x - priority)) 
     | Min -> aiListWeight |> List.minBy snd 
     | Max -> aiListWeight |> List.maxBy snd 
     | Average -> 
      let average = aiListWeight |> List.averageBy snd 
      aiListWeight 
      |> List.minBy (fun (ai, priority) -> abs(average - priority)) 

type AI_Choose = 
    | AI_Priority of list<AI> * Condition 
    | AI_Weighted_Priority of list<AI * float> * Condition 

    // I'm sad that I can't do this  
    let mutable chosen = Option<AI>.None 

    member this.Choose() = 
     match this with 
     | AI_Priority(aiList, condition) -> 
      aiList 
      |> List.map (fun ai -> ai, ai.Priority()) 
      |> condition.Select 
      |> fst 
     | AI_Weighted_Priority(aiList, condition) -> 
      aiList 
      |> List.map (fun (ai, weight) -> ai, weight * ai.Priority()) 
      |> condition.Select 
      |> fst 

    member this.Chosen 
     with get() = 
      if Option.isNone chosen then 
       chosen <- Some(this.Choose()) 
      chosen.Value 
     and set(x) = 
      if Option.isSome chosen then 
       chosen.Value.Stop() 
      chosen <- Some(x) 
      x.Start() 

    interface AI with 
     member this.Start() = 
      this.Chosen.Start() 
     member this.Stop() = 
      this.Chosen.Stop() 
     member this.Reset() = 
      this.Chosen <- this.Choose() 
     member this.Priority() = 
      this.Chosen.Priority() 
     member this.Update(gameTime) = 
      this.Chosen.Update(gameTime) 

Odpowiedz

2

byłoby sensowne, aby pozwolić "pozwolić" związać się w dyskryminowanych związkach. Myślę, że powodem, dla którego nie jest to możliwe, jest to, że dyskryminowane związki wciąż są oparte na projekcie OCaml, podczas gdy obiekty pochodzą ze świata .NET. F # stara się zintegrować te dwa tak bardzo, jak to możliwe, ale prawdopodobnie mogłoby pójść dalej.

W każdym razie, wydaje mi się, że używasz związku dyskryminacji tylko do implementacji wewnętrznego zachowania typu AI_Choose. W takim przypadku można zadeklarować odrębny związek oddzielnie i użyć go do zaimplementowania typu obiektu.

wierzę, można napisać coś takiego:

type AiChooseOptions = 
    | AI_Priority of list<AI> * Condition 
    | AI_Weighted_Priority of list<AI * float> * Condition 

type AiChoose(aiOptions) = 
    let mutable chosen = Option<AI>.None 
    member this.Choose() = 
     match aiOptions with 
     | AI_Priority(aiList, condition) -> (...) 
     | AI_Weighted_Priority(aiList, condition) -> (...) 
    member this.Chosen (...) 
    interface AI with (...) 

Kluczową różnicą między klasowej hierarchii i dyskryminowanych związków jest, jeśli chodzi o rozciągliwości. Klasy ułatwiają dodawanie nowych typów, podczas gdy dyskryminowane związki ułatwiają dodawanie nowych funkcji, które działają z typem (w twoim przypadku AiChooseOptions), więc jest to prawdopodobnie pierwsza rzecz, którą należy wziąć pod uwagę przy projektowaniu aplikacji.

3

Dla wszystkich zainteresowanych skończyło się pochodzący AI_Priority i AI_Weighted_Priority z abstrakcyjnej klasy bazowej.

[<AbstractClass>] 
type AI_Choose() = 
    let mutable chosen = Option<AI>.None 

    abstract member Choose : unit -> AI 

    member this.Chosen 
     with get() = 
      if Option.isNone chosen then 
       chosen <- Some(this.Choose()) 
      chosen.Value 
     and set(x) = 
      if Option.isSome chosen then 
       chosen.Value.Stop() 
      chosen <- Some(x) 
      x.Start() 

    interface AI with 
     member this.Start() = 
      this.Chosen.Start() 
     member this.Stop() = 
      this.Chosen.Stop() 
     member this.Reset() = 
      this.Chosen <- this.Choose() 
     member this.Priority() = 
      this.Chosen.Priority() 
     member this.Update(gameTime) = 
      this.Chosen.Update(gameTime) 

type AI_Priority(aiList : list<AI>, condition : Condition) = 
    inherit AI_Choose() 
    override this.Choose() = 
     aiList 
     |> List.map (fun ai -> ai, ai.Priority()) 
     |> condition.Select 
     |> fst 

type AI_Weighted_Priority(aiList : list<AI * float>, condition : Condition) = 
    inherit AI_Choose() 
    override this.Choose() = 
     aiList 
     |> List.map (fun (ai, weight) -> ai, weight * ai.Priority()) 
     |> condition.Select 
     |> fst 
3

Powracając do tego kodu, znalazłem sugestię Tomasa, która okazała się o wiele czystsza.

type AiChooseOptions = 
    | Priority of List<AI * Priority> 
    | WeightedPriority of List<AI * Priority * float> 
    member this.Choose(condition : Condition) = 
     match this with 
     | Priority(list) -> 
      list 
      |> List.map (fun (ai, priority) -> ai, priority.Priority()) 
      |> condition.Select 
     | WeightedPriority(list) -> 
      list 
      |> List.map (fun (ai, p, weight) -> ai, p.Priority() * weight) 
      |> condition.Select 

type AiChoose(condition, list : AiChooseOptions) = 
    let mutable chosen = Unchecked.defaultof<AI>, 0.0 

    interface AI with 
     member this.Update(gameTime) = 
      (fst chosen).Update(gameTime) 

    interface Priority with 
     member this.Priority() = 
      chosen <- list.Choose(condition) 
      (snd chosen)