Discriminada Unión y dejar que la unión?
-
19-09-2019 - |
Pregunta
fijaciones Por qué no dejar que se permiten en una unión discriminada? Asumo que tiene que ver con fijaciones vamos siendo ejecutado en un constructor por defecto?
En una nota secundaria alguna sugerencia sobre cómo podría reescribir AI_Choose
sería apreciada. Quiero mantener la prioridad ponderada en una tupla con la IA. Mi idea es tener AI_Weighted_Priority
hereda AI_Priority
y anular elegir. No quiero hacer frente a comprimir listas de diferentes longitudes (mala práctica de la OMI.)
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)
Solución
que tendría sentido para permitir que "dejar" de unión uniones dentro discriminados. Creo que la razón por la que no es posible es que los sindicatos discriminados todavía se basan en el diseño OCaml mientras que los objetos provienen del mundo .NET. F # está tratando de integrar estos dos tanto como sea posible, pero probablemente podría ir más allá.
De todos modos, me parece que está utilizando la unión discriminante única para poner en práctica un comportamiento interno del tipo AI_Choose
. En ese caso, se podría declarar una unión discriminada por separado y utilizarlo para poner en práctica el tipo de objeto.
Creo que podría escribir algo como esto:
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 diferencia clave entre la jerarquía de clases y los sindicatos discriminados es cuando se trata de extensibilidad. Las clases hacen que sea más fácil añadir nuevos tipos mientras que los sindicatos discriminados hacen que sea más fácil añadir nuevas funciones que trabajan con el tipo (en su caso) AiChooseOptions, por lo que es probablemente la primera cosa a considerar en el diseño de la aplicación.
Otros consejos
Para cualquier persona interesada que terminó derivando AI_Priority
y AI_Weighted_Priority
de una clase base abstracta.
[<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
Revisando el código acabé teniendo la sugerencia de Tomás, que resultó mucho más limpio.
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)