Haskell中模拟对象的身份

时间:2013-10-07 16:09:50

标签: haskell

使用面向对象语言编写模拟,每个对象都有一个标识 - 即,即使其他对象具有完全相同的属性,也可以将其与模拟中的每个其他对象区分开来。无论物体随时间变化多少,物体都会保留其身份。这是因为每个对象在内存中都有一个唯一的位置,我们可以使用指针或引用来表达该位置。即使您没有强制使用像GUID这样的额外身份系统,这也可行。 (你经常会这样做,以支持网络或数据库之类的东西,这些东西不能用指针来思考。)

我不相信Haskell中有一个相同的概念。那么,标准方法是使用类似GUID的东西吗?

更新以澄清问题:身份是我的问题域中的一个重要概念,其原因之一是:对象之间具有关系,并且必须保留这些关系。例如,Haskell通常会说红色汽车是红色汽车,所有红色汽车都是相同的(前提是颜色是汽车的唯一属性)。但是,如果每辆红色汽车必须与其所有者相关联呢?如果车主可以重新粉刷他的车怎么办?

最终更新综合答案:一致认为,如果模拟的某些部分实际上会使用这些标识符,那么您应该只向数据类型添加标识符,并且没有其他标识符表达相同信息的方式。例如。对于一个人拥有多辆汽车的情况,每辆汽车都有一种颜色,一个人可以保留一份不可变的汽车清单。只要您有权访问此人,就完全表达了所有权关系。

Person可能需要也可能不需要某种唯一标识符。出现这种情况的一种情况是:有一个函数可以获取Car和所有人的集合,并在适当的Person上强制使用ParkingTicket。汽车的颜色无法唯一识别获得机票的人。所以我们可以给这个人一个ID并让汽车存储它。

但即使这样也可以通过更好的设计来避免。也许我们的汽车现在有一个类型为ParkingPosition的附加属性,可以评估为合法或非法。因此,我们将人员集合传递给一个函数,该函数查看每个人的汽车列表,检查每个人的ParkingPosition,并在适当的情况下对该人员施加ParkingTicket。由于此功能始终知道它正在查看哪个人,因此Car无需记录该信息。

因此,在许多情况下,分配ID并不像最初看起来那样必要。

4 个答案:

答案 0 :(得分:3)

我的方法是将所有状态信息存储在数据记录中,例如

data ObjState = ObjState
    { objStName :: String
    , objStPos :: (Int, Int)
    , objStSize :: (Int, Int)
    } deriving (Eq, Show)

data Obj = Obj
    { objId :: Int
    , objState :: ObjState
    } deriving (Show)

instance Eq Obj where
    obj1 == obj2 = objId obj1 == objId obj2

状态应该由API /库/应用程序管理。如果你需要真正的指向可变结构的指针,那么它就有内置的库,但它们被认为是不安全的并且使用起来很危险,除非你知道你在做什么(即便如此,你必须要谨慎)。有关详细信息,请查看Foreign中的base个模块。

答案 1 :(得分:3)

为什么要“解决”这个无问题?对象标识是一个带有OO语言的问题,Haskell很乐意避免这种语言。

在一个不可变对象的世界中,两个具有相同值的对象是同一个对象。将相同的不可变对象放入列表中两次,并且您有两个不同的对象,无论您希望以何种方式查看事物(例如,它们“都”对元素总数有贡献,并且它们具有唯一索引)而没有任何问题Java风格的引用相等导致。如果愿意,您甚至可以将该列表保存到数据库表中并获取两个不同的行。你还需要什么?

<强>更新

Jarret,您似乎确信模型中的对象必须具有真正独立的身份,因为现实生活中的对象将是截然不同的。但是,在模拟中,这仅在需要区分对象的上下文中很重要。通常,您只需要在必须区分和跟踪对象的上下文中使用唯一标识符,而不是在这些上下文之外。因此,确定这些上下文,将重要对象的生命周期映射到模拟(而不是“真实”世界),并创建适当的标识符。

您一直在为自己的问题提供答案。如果汽车拥有车主,那么鲍勃的红色车可以与卡罗尔的红色汽车区别开来。如果鲍勃可以重新粉刷他的车,你可以用蓝色汽车替换他的红色汽车。如果

,您只需要更多
  1. 你的模拟车没有车主
  2. 您需要能够区分一辆无车的红色轿车。
  3. 在一个简单的模型中,1可能是真的而2则不是。在这种情况下,所有无车的红色汽车都是相同的红色汽车所以为什么要打扰使它们与众不同?

    在导弹模拟中,为什么导弹需要跟踪他们自己的发射器?他们不是针对他们的发射器!如果发射器在发射后可以继续控制导弹,则发射器需要知道它拥有哪种导弹,但反之则不然。导弹只需知道它的轨迹和目标。当它降落并爆炸时,拥有者的意义是什么?如果它是从发射器A而不是发射器B发射的,它会发出更大的声音吗?

    你的发射器可以是空的,也可以发射n枚导弹。它有一个位置。目标有位置。在任何时候,飞行中都有k枚导弹;每枚导弹都有一个位置,一个速度/轨迹和一个爆发力。任何位置与地面重合的导弹都应该变成爆炸导弹,或者等等。

    在每种情况下,哪些信息很重要?在爆炸时,发射器身份真的重要吗?为什么?敌人是否会发动报复性罢工?没有?那不是爆炸的重要信息。在启动之后,它可能甚至不是重要的信息。发射只是一个步骤,其中属于发射器A的导弹数量减少,而飞行中的导弹数量增加。

    现在,您可能对这些问题有一个很好的答案,但是在开始使用他们可能不需要的身份开始笨拙的对象之前,您应该完全映射模型。

答案 2 :(得分:1)

在Haskell中,价值观和身份的概念是分离的。所有变量都只是对值的不可变绑定。

有一些类型的值是对另一个值的可变引用,例如IORefMVarTVar,这些可以用作身份。

您可以通过比较两个MVar和一个等式检查来比较它们的引用值来执行身份检查。

Rich Hickey的精彩演讲详细介绍了这个问题:http://www.infoq.com/presentations/Value-Values

答案 3 :(得分:0)

你总是可以写:

> let mylist = take 25 $ cycle "a"
> mylist
"aaaaaaaaaaaaaaaaaaaaaaaaa"
> zip mylist [1..]
[('a',1),('a',2),('a',3),('a',4),('a',5),('a',6),('a',7),('a',8),('a',9),('a',10),
 ('a',11),('a',12),('a',13),('a',14),('a',15),('a',16),('a',17),('a',18),('a',19),
 ('a',20),('a',21),('a',22),('a',23),('a',24),('a',25)]

如果我们不是在开玩笑 - 将其保存为数据的一部分

data MyObj = MyObj {id ::Int, ...}

<强>已更新

如果我们想分别处理颜色和id,我们可以在Haskell中做下一步:

data Color = Green | Red | Blue deriving (Eq, Show)

data Car = Car {carid :: Int, color :: Color} deriving (Show)

garage = [Car 1 Green, Car 2 Green, Car 3 Red]

redCars = filter ((== Red) . color) garage

greenCars = filter ((== Green) . color) garage

paint2Blue car = car {color=Blue}

isGreen = (== Green) . color

newGarage = map (\car -> if isGreen car then paint2Blue car else car) garage

在gchi中看到结果:

> garage
[Car {carid = 1, color = Green},Car {carid = 2, color = Green},Car {carid = 3, color = Red}]
> redCars
[Car {carid = 3, color = Red}]
> greenCars
[Car {carid = 1, color = Green},Car {carid = 2, color = Green}]
> newGarage
[Car {carid = 1, color = Blue},Car {carid = 2, color = Blue},Car {carid = 3, color = Red}]