Discriminé Union et laisser lier?
-
19-09-2019 - |
Question
Pourquoi les laisser fixations non autorisées dans une union de discrimination? Je suppose qu'il doit faire avec liaisons let en cours d'exécution dans un constructeur par défaut?
Sur une note secondaire des suggestions sur la façon dont je pourrais réécrire AI_Choose
serait apprécié. Je veux garder la priorité pondérée dans un tuple avec l'IA. Mon idée est d'avoir AI_Weighted_Priority
Hériter AI_Priority
et passer outre Choisissez. Je ne veux pas traiter avec des listes zipper de différentes longueurs (mauvaise pratique 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)
La solution
il serait logique de permettre « laisser » liant les syndicats à l'intérieur discriminées. Je pense que la raison pour laquelle il est impossible que les syndicats discriminés sont toujours basés sur la conception OCaml alors que les objets viennent du monde .NET. F # tente d'intégrer ces deux autant que possible, mais il pourrait probablement aller plus loin.
Quoi qu'il en soit, il me semble que vous utilisez l'union discriminante que pour mettre en œuvre un comportement interne du type AI_Choose
. Dans ce cas, vous pouvez déclarer une union discrimination séparément et l'utiliser pour mettre en œuvre le type d'objet.
Je crois que vous pourriez écrire quelque chose comme ceci:
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 (...)
La principale différence entre la hiérarchie des classes et les syndicats discriminés est en ce qui concerne l'extensibilité. Les classes facilitent l'ajout de nouveaux types tandis que les syndicats discriminés facilitent l'ajout de nouvelles fonctions qui fonctionnent avec le type (dans votre AiChooseOptions de cas), de sorte que est probablement la première chose à considérer lors de la conception de l'application.
Autres conseils
Pour toute personne intéressée, je fini par tirer AI_Priority
et AI_Weighted_Priority
d'une classe de base abstraite.
[<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
Revisiter ce code, je fini par prendre la suggestion de Tomas qui se un beaucoup plus propre.
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)