【发布时间】:2011-11-18 08:13:12
【问题描述】:
我想更新一个嵌套的、不可变的数据结构(我附上了一个假设游戏的小例子。)我想知道这是否可以更优雅地完成。
每当地牢中的某些东西发生变化时,我们都需要一个新的地牢。所以,我给了它一个普通的更新成员。对于一般情况,我可以想到的最佳使用方法是为每个嵌套指定处理函数,然后将组合函数传递给更新成员。
然后,对于非常常见的情况(例如将地图应用于特定级别的所有怪物),我会提供额外的成员 (Dungeon.MapMonstersOnLevel)。
整个事情都有效,我只是想知道,如果有人能想出更好的方法来做这件事。
谢谢!
// types
type Monster(awake : bool) =
member this.Awake = awake
type Room(locked : bool, monsters : Monster list) =
member this.Locked = locked
member this.Monsters = monsters
type Level(illumination : int, rooms : Room list) =
member this.Illumination = illumination
member this.Rooms = rooms
type Dungeon(levels : Level list) =
member this.Levels = levels
member this.Update levelFunc =
new Dungeon(this.Levels |> levelFunc)
member this.MapMonstersOnLevel (f : Monster -> Monster) nLevel =
let monsterFunc = List.map f
let roomFunc = List.map (fun (room : Room) -> new Room(room.Locked, room.Monsters |> monsterFunc))
let levelFunc = List.mapi (fun i (level : Level) -> if i = nLevel then new Level(level.Illumination, level.Rooms |> roomFunc) else level)
new Dungeon(this.Levels |> levelFunc)
member this.Print() =
this.Levels
|> List.iteri (fun i e ->
printfn "Level %d: Illumination %d" i e.Illumination
e.Rooms |> List.iteri (fun i e ->
let state = if e.Locked then "locked" else "unlocked"
printfn " Room %d is %s" i state
e.Monsters |> List.iteri (fun i e ->
let state = if e.Awake then "awake" else "asleep"
printfn " Monster %d is %s" i state)))
// generate test dungeon
let m1 = new Monster(true)
let m2 = new Monster(false)
let m3 = new Monster(true)
let m4 = new Monster(false)
let m5 = new Monster(true)
let m6 = new Monster(false)
let m7 = new Monster(true)
let m8 = new Monster(false)
let r1 = new Room(true, [ m1; m2 ])
let r2 = new Room(false, [ m3; m4 ])
let r3 = new Room(true, [ m5; m6 ])
let r4 = new Room(false, [ m7; m8 ])
let l1 = new Level(100, [ r1; r2 ])
let l2 = new Level(50, [ r3; r4 ])
let dungeon = new Dungeon([ l1; l2 ])
dungeon.Print()
// toggle wake status of all monsters
let dungeon1 = dungeon.MapMonstersOnLevel (fun m -> new Monster(not m.Awake)) 0
dungeon1.Print()
// remove monsters that are asleep which are in locked rooms on levels where illumination < 100 and unlock those rooms
let monsterFunc2 = List.filter (fun (monster : Monster) -> monster.Awake)
let roomFunc2 = List.map(fun (room : Room) -> if room.Locked then new Room(false, room.Monsters |> monsterFunc2) else room)
let levelFunc2 = List.map(fun (level : Level) -> if level.Illumination < 100 then new Level(level.Illumination, level.Rooms |> roomFunc2) else level)
let dungeon2 = dungeon.Update levelFunc2
dungeon2.Print()
【问题讨论】:
-
您是否考虑过使用记录或单大小写联合类型?
-
@RamonSnir:我尝试在下面的 pad 回答中解释自己。