Дискриминационный союз и пусть обязательный? - PullRequest
6 голосов
/ 26 августа 2009

Почему в дискриминационном союзе запрещены привязки? Я предполагаю, что это связано с тем, что привязки выполняются в конструкторе по умолчанию?

На второй ноте любые предложения о том, как я мог бы переписать AI_Choose, были бы оценены. Я хочу сохранить взвешенный приоритет в кортеже с ИИ. Моя идея состоит в том, чтобы AI_Weighted_Priority наследовать AI_Priority и переопределить выбор. Я не хочу иметь дело с почтовыми списками разной длины (плохая практика 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)

Ответы [ 3 ]

3 голосов
/ 06 октября 2009

Возвращаясь к этому коду, я принял предложение Томаса, которое оказалось намного чище.

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)
3 голосов
/ 26 августа 2009

Для всех, кто заинтересовался, я получил AI_Priority и AI_Weighted_Priority из абстрактного базового класса.

[<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
2 голосов
/ 27 августа 2009

было бы разумно разрешить привязку «позволять» внутри дискриминационных союзов. Я думаю, что причина, по которой это невозможно, заключается в том, что дискриминационные союзы по-прежнему основаны на дизайне OCaml, а объекты приходят из мира .NET. F # пытается объединить эти два в максимально возможной степени, но это могло бы пойти дальше.

В любом случае, мне кажется, что вы используете дискриминационное объединение только для реализации некоторого внутреннего поведения типа AI_Choose. В этом случае вы можете объявить различающееся объединение отдельно и использовать его для реализации типа объекта.

Полагаю, вы могли бы написать что-то вроде этого:

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

Ключевое различие между иерархией классов и дискриминационными объединениями заключается в том, что касается расширяемости. Классы облегчают добавление новых типов, в то время как различимые объединения облегчают добавление новых функций, которые работают с типом (в вашем случае AiChooseOptions), поэтому, вероятно, это первое, что следует учитывать при разработке приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...