2009-08-26 8 views
6

Warum sind Bindungen in einer diskriminierten Union nicht erlaubt? Ich nehme an, es hat damit zu tun, dass Bindungen in einem Standardkonstruktor ausgeführt werden.Diskriminierte Union & binden lassen?

Auf einer sekundären Anmerkung würden irgendwelche Vorschläge, wie ich AI_Choose umschreiben könnte, geschätzt werden. Ich möchte die gewichtete Priorität in einem Tupel mit der KI behalten. Meine Idee ist AI_Weighted_Priority erben AI_Priority und überschreiben zu wählen. Ich will nicht mit zippen Listen unterschiedlicher Länge bewältigen (imo schlechte Praxis.)

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) 

Antwort

2

es würde Sinn machen, "let" in diskriminierten Verbindungen zu binden. Ich denke, der Grund, warum es nicht möglich ist, ist, dass diskriminierte Gewerkschaften immer noch auf dem OCaml-Design basieren, während Objekte aus der .NET-Welt stammen. F # versucht, diese beiden so weit wie möglich zu integrieren, aber es könnte wahrscheinlich noch weiter gehen.

Wie auch immer, es scheint mir, dass Sie die diskriminierende Union nur verwenden, um einige interne Verhalten des Typs AI_Choose zu implementieren. In diesem Fall könnten Sie eine diskriminierte Union separat deklarieren und damit den Objekttyp implementieren.

Ich glaube, Sie so etwas schreiben könnte:

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 (...) 

Der wesentliche Unterschied zwischen Klassenhierarchie und diskriminiert Gewerkschaften, wenn es um Dehnbarkeit kommt. Klassen erleichtern das Hinzufügen neuer Typen, während diskriminierte Vereinigungen das Hinzufügen neuer Funktionen erleichtern, die mit dem Typ arbeiten (in Ihrem Fall AiChooseOptions), was beim Entwurf der Anwendung wahrscheinlich zuerst berücksichtigt wird.

3

Für alle Interessierten ich AI_Priority und AI_Weighted_Priority von einer abstrakten Basisklasse abgeleitet endete.

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

Als ich diesen Code erneut besuchte, nahm ich schließlich Tomas Vorschlag an, der viel sauberer wurde.

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)