gpt4 book ai didi

types - 派生类型的模式匹配对于 F# 来说是惯用的吗?

转载 作者:行者123 更新时间:2023-12-04 22:57:55 25 4
gpt4 key购买 nike

我想以最惯用的方式实现以下内容

玩家在 map 上。

  • 如果他与箭处于相同位置,他会受到 1 点伤害
  • 如果他与一个生物处于同一位置,他会受到等于生物生命值的伤害
  • 如果他与硬币处于相同位置,他将获得 1$
  • 如果他与药物处于相同的位置,他会治愈 1

  • 这是为交互编写的 stub :
    open System
    [<AbstractClass>]
    type ActorBase(x,y,symbol)=
    member this.X:int=x
    member this.Y:int=y
    member this.Symbol:char=symbol

    type Medication(x,y)=
    inherit ActorBase(x,y,'♥')
    type Coin(x,y)=
    inherit ActorBase(x,y,'$')

    type Arrow(x,y,symbol,targetX,targetY) =
    inherit ActorBase(x,y,symbol)
    member this.TargetX=targetX
    member this.TargetY=targetY

    [<AbstractClass>]
    type CreatureBase(x,y,symbol,hp) =
    inherit ActorBase(x,y,symbol)
    member this.HP:int=hp

    type Player(x,y,hp, score) =
    inherit CreatureBase(x,y,'@',hp)
    member this.Score = score
    type Zombie(x,y,hp,targetX,targetY) =
    inherit CreatureBase(x,y,'z',hp)
    member this.TargetX=targetX
    member this.TargetY=targetY

    let playerInteraction (player:Player) (otherActor:#ActorBase):unit =
    printfn "Interacting with %c" otherActor.Symbol
    match (otherActor :> ActorBase) with
    | :? CreatureBase as creature -> printfn "Player is hit by %d by creature %A" (creature.HP) creature
    | :? Arrow -> printfn "Player is hit by 1 by arrow"
    | :? Coin -> printfn "Player got 1$"
    | :? Medication -> printfn "Player is healed by 1"
    | _ -> printfn "Interaction is not recognized"

    let otherActorsWithSamePosition (actor:#ActorBase) =
    seq{
    yield new Zombie(0,0,3,1,1) :> ActorBase
    yield new Zombie(0,1,3,1,1) :> ActorBase
    yield new Arrow(0,0,'/',1,1) :> ActorBase
    yield new Coin(0,0) :> ActorBase
    yield new Medication(0,0) :> ActorBase
    }
    |> Seq.where(fun a -> a.X=actor.X && a.Y=actor.Y)
    [<EntryPoint>]
    let main argv =
    let player = new Player(0,0,15,0)
    for actor in (otherActorsWithSamePosition player) do
    playerInteraction player actor
    Console.ReadLine() |> ignore
    0

    1) 类和继承是否要在 F# 中使用?或者它们只是为了与.Net 兼容?我是否应该使用记录来代替,如果是,如何?

    2) 在 C# 中切换类型被认为是一种不好的做法。 F#也一样吗?如果是,我应该写什么而不是 otherActorsWithSamePosition ?实现 otherXsWithSamePosition每个类(class) X从 Actor 派生看起来不像可扩展的解决方案

    更新:

    我试图用一个有区别的联合来实现它,但没有设法编译:
    type IActor =
    abstract member X:int
    abstract member Y:int
    abstract member Symbol:char
    type IDamagable =
    abstract member Damaged:int->unit
    type IDamaging =
    abstract member Damage:int
    type Player =
    {
    X:int
    Y:int
    HP:int
    Score:int
    }
    interface IActor with
    member this.X=this.X
    member this.Y=this.Y
    member this.Symbol='@'
    interface IDamagable with
    member this.Damaged damage = printfn "The player is damaged by %d" damage
    interface IDamaging with
    member this.Damage = this.HP
    type Coin =
    {
    X:int
    Y:int
    }
    interface IActor with
    member this.X=this.X
    member this.Y=this.Y
    member this.Symbol='$'
    type Medication =
    {
    X:int
    Y:int
    }
    interface IActor with
    member this.X=this.X
    member this.Y=this.Y
    member this.Symbol='♥'
    type Arrow =
    {
    X:int
    Y:int
    DestinationX:int
    DestinationY:int
    Symbol:char
    }
    interface IActor with
    member this.X=this.X
    member this.Y=this.Y
    member this.Symbol=this.Symbol
    interface IDamaging with
    member this.Damage = 1
    type Zombie =
    {
    X:int
    Y:int
    DestinationX:int
    DestinationY:int
    HP:int
    }
    interface IActor with
    member this.X=this.X
    member this.Y=this.Y
    member this.Symbol='z'
    interface IDamaging with
    member this.Damage = this.HP
    type Actor =
    |Player of Player
    |Coin of Coin
    |Zombie of Zombie
    |Medication of Medication
    |Arrow of Arrow
    let otherActorsWithSamePosition (actor:Actor) =
    seq{
    yield Zombie {X=0;Y=0; HP=3;DestinationX=1;DestinationY=1}
    yield Zombie {X=0;Y=1; HP=3;DestinationX=1;DestinationY=1}
    yield Arrow {X=0;Y=0; Symbol='/';DestinationX=1;DestinationY=1}
    yield Coin {X=0;Y=0}
    yield Medication {X=0;Y=0}
    }
    //Cannot cast to interface
    |> Seq.where(fun a -> (a:>IActor).X=actor.X && (a:>IActor).Y=actor.Y)
    let playerInteraction player (otherActor:Actor) =
    match otherActor with
    | Coin coin -> printfn "Player got 1$"
    | Medication medication -> printfn "Player is healed by 1"
    //Cannot check this
    | :?IDamaging as damaging -> (player:>IDamagable).Damaged(damaging.Damage)

    [<EntryPoint>]
    let main argv =
    let player = Player {X=0;Y=0;HP=15;Score=0}
    for actor in (otherActorsWithSamePosition player) do
    playerInteraction player actor
    Console.ReadLine() |> ignore
    0

    问题:

    1)更重要的是:

    我无法对现有记录进行有区别的联合
    Actor =
    | Medication {x:int;y:int;symbol:char}

    引发关于不推荐使用的构造的错误
    type Medication = {x:int;y:int;symbol:char}
    Actor =
    | Medication

    认为 MedicationActor.Medication不同种类

    我使用了一个相当丑陋的构造
    type Medication = {x:int;y:int;symbol:char}
    Actor =
    | Medication of Medication

    但它阻止我在接口(interface)上匹配。

    2) F# 中没有隐式接口(interface)实现。这条鳕鱼已经有很多样板元素,比如'member this.X=this.X'。对于比“IActor”更复杂的东西,它会产生越来越多的问题。

    您能否提供一个在 F# 中正确使用可区分联合参数的命名参数的示例?在这种情况下有帮助吗?

    最佳答案

    Is pattern matching on derived types idiomatic for F#?



    我会说不,因为您有一个基本的谬论,即对象层次结构中的类型代码是惯用的 F#。

    我不认为类型的对象层次结构是惯用的 F# 或功能性的。所以这个问题是无效的。我从历史的角度来看 F# 来自 ML 和 OCaml,而不是来自 OO 方面。正如我在编写函数式代码时总是建议的那样,忘记你对 OO 的了解,因为它只会让你走上困惑的道路。如果您必须与 OO 交互,那么您将不得不硬着头皮但尽可能将 OO 排除在外。

    Are classes and inheritance meant to be used in F#?
    Or are they just for compatibility with .Net?



    如果您查看 F# classes 上的 MSDN 文章在该部分下 When to Use Classes, Unions, Records, and Structures你会看见

    Given the variety of types to choose from, you need to have a good understanding of what each type is designed for to select the appropriate type for a particular situation. Classes are designed for use in object-oriented programming contexts. Object-oriented programming is the dominant paradigm used in applications that are written for the .NET Framework. If your F# code has to work closely with the .NET Framework or another object-oriented library, and especially if you have to extend from an object-oriented type system such as a UI library, classes are probably appropriate.

    If you are not interoperating closely with object-oriented code, or if you are writing code that is self-contained and therefore protected from frequent interaction with object-oriented code, you should consider using records and discriminated unions. A single, well thought–out discriminated union, together with appropriate pattern matching code, can often be used as a simpler alternative to an object hierarchy. For more information about discriminated unions, see Discriminated Unions (F#).



    .

    Should I use records instead, and, if yes, how?



    首先不要使用有区别的联合,然后如果数据变得更复杂,请查看记录。在某些情况下,我经常使用记录,但大多数时候不使用。这是 it depends问题。

    Records have the advantage of being simpler than classes, but records are not appropriate when the demands of a type exceed what can be accomplished with their simplicity. Records are basically simple aggregates of values, without separate constructors that can perform custom actions, without hidden fields, and without inheritance or interface implementations. Although members such as properties and methods can be added to records to make their behavior more complex, the fields stored in a record are still a simple aggregate of values. For more information about records, see Records (F#).

    Structures are also useful for small aggregates of data, but they differ from classes and records in that they are .NET value types. Classes and records are .NET reference types. The semantics of value types and reference types are different in that value types are passed by value. This means that they are copied bit for bit when they are passed as a parameter or returned from a function. They are also stored on the stack or, if they are used as a field, embedded inside the parent object instead of stored in their own separate location on the heap. Therefore, structures are appropriate for frequently accessed data when the overhead of accessing the heap is a problem. For more information about structures, see Structures (F#).



    .

    Switching on types is considered a bad practice in C#. Is it the same for F#?



    查看 Is pattern matching on derived types idiomatic for F#? 的答案

    How?


    namespace Game

    type Location = int * int
    type TargetLocation = Location
    type CurrentLocation = Location
    type Symbol = char
    type ActorType = CurrentLocation * Symbol
    type HitPoints = int
    type Health = int
    type Money = int
    type Creature = ActorType * HitPoints

    // Player = Creature * Health * Money
    // = (ActorType * HitPoints) * Health * Money
    // = ((CurrentLocation * Symbol) * HitPoints) * Health * Money
    // = ((Location * Symbol) * HitPoints) * Health * Money
    // = (((int * int) * char) * int) * int * int
    type Player = Creature * Health * Money

    type Actor =
    | Medication of ActorType
    | Coin of ActorType
    | Arrow of Creature * TargetLocation // Had to give arrow hit point damage
    | Zombie of Creature * TargetLocation

    module main =

    [<EntryPoint>]
    let main argv =

    let player = ((((0,0),'p'),15),0,0)

    let actors : Actor List =
    [
    Medication((0,0),'♥');
    Zombie((((3,2),'Z'),3),(0,0));
    Zombie((((5,1),'Z'),3),(0,0));
    Arrow((((4,3),'/'),3),(2,1));
    Coin((4,2),'$');
    ]

    let updatePlayer player (actors : Actor list) : Player =
    let interact (((((x,y),symbol),hitPoints),health,money) : Player) otherActor =
    match (x,y),otherActor with
    | (playerX,playerY),Zombie((((opponentX,opponentY),symbol),zombieHitPoints),targetLocation) when playerX = opponentX && playerY = opponentY ->
    printfn "Player is hit by creature for %i hit points." zombieHitPoints
    ((((x,y),symbol),hitPoints - zombieHitPoints),health,money)
    | (playerX,playerY),Arrow((((opponentX,opponentY),symbol),arrowHitPoints),targetLocation) when playerX = opponentX && playerY = opponentY ->
    printfn "Player is hit by arrow for %i hit points." arrowHitPoints
    ((((x,y),symbol),hitPoints - arrowHitPoints),health,money)
    | (playerX,playerY),Coin((opponentX,opponentY),symbol) when playerX = opponentX && playerY = opponentY ->
    printfn "Player got 1$."
    ((((x,y),symbol),hitPoints),health,money + 1)
    | (playerX,playerY),Medication((opponentX,opponentY),symbol) when playerX = opponentX && playerY = opponentY ->
    printfn "Player is healed by 1."
    ((((x,y),symbol),hitPoints),health+1,money)
    | _ ->
    // When we use guards in matching, i.e. when clause, F# requires a _ match
    ((((x,y),symbol),hitPoints),health,money)
    let rec updatePlayerInner player actors =
    match actors with
    | actor::t ->
    let player = interact player actor
    updatePlayerInner player t
    | [] -> player
    updatePlayerInner player actors

    let rec play player actors =
    let player = updatePlayer player actors
    play player actors

    // Since this is example code the following line will cause a stack overflow.
    // I put it in as an example function to demonstrate how the code can be used.
    // play player actors

    // Test

    let testActors : Actor List =
    [
    Zombie((((0,0),'Z'),3),(0,0))
    Arrow((((0,0),'/'),3),(2,1))
    Coin((0,0),'$')
    Medication((0,0),'♥')
    ]

    let updatedPlayer = updatePlayer player testActors

    printf "Press any key to exit: "
    System.Console.ReadKey() |> ignore
    printfn ""

    0 // return an integer exit code

    由于这不是一个完整的游戏,我做了一些测试来展示玩家与其他 Actor 互动的基础知识。
    Player is hit by creature for 3 hit points.
    Player is hit by arrow for 3 hit points.
    Player got 1$.
    Player is healed by 1.

    如果您对它的工作原理有具体问题,请提出一个新问题并返回此问题。

    希望来自 OO 的人会明白为什么我们这些转向函数式编程的人会喜欢它,以及为什么在从头开始编写函数式代码时你不应该有任何 OO 的想法。同样,如果您正在与其他 OO 代码交互,那么有 OO 的想法就可以了。

    关于types - 派生类型的模式匹配对于 F# 来说是惯用的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36529293/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com