3

用面向对象的语言编写模拟,每个对象都有一个标识——也就是说,一种将它与模拟中的每个其他对象区分开来的方法,即使其他对象具有完全相同的属性。一个对象保留它的身份,无论它随着时间的推移发生多少变化。这是因为每个对象在内存中都有一个唯一的位置,我们可以用指针或引用来表达那个位置。即使您不强加像 GUID 这样的附加身份系统,这也有效。(您经常会这样做以支持不考虑指针的网络或数据库之类的东西。)

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

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

综合答案的最终更新:共识似乎是,如果模拟的某些部分实际使用这些标识符,您应该只向数据类型添加标识符,并且没有其他方法可以表达相同的信息。例如,对于一个人拥有多个汽车的情况,每个汽车都有一个颜色,一个人可以保留一个不可变汽车的列表。只要您有权访问 Person,这充分表达了所有权关系。

Person 可能需要也可能不需要某种唯一标识符。发生这种情况的一种情况是:有一个函数接受 Car 和所有 Person 的集合,并将 ParkingTicket 强加给适当的 Person。汽车的颜色不能唯一地识别获得票的人。所以我们可以给 Person 一个 ID 并让 Car 存储它。

但即使是这种情况,也可以通过更好的设计来避免。也许我们的汽车现在有一个类型为 ParkingPosition 的附加属性,可以将其评估为合法或非法。因此,我们将 Person 集合传递给一个函数,该函数查看每个 Person 的 Cars 列表,检查每个人的 ParkingPosition,并在适当的情况下将 ParkingTicket 强加给那个 Person。因为这个函数总是知道它在看哪个 Person,所以 Car 不需要记录那个信息。

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

4

4 回答 4

3

为什么要“解决”这个非问题?对象身份是 OO 语言的一个问题,Haskell 很乐意避免。

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

更新

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

你不断为自己的问题提供答案。如果汽车有所有者,那么鲍勃的红色汽车可以与卡罗尔的红色汽车区分开来。如果鲍勃可以重新粉刷他的车,您可以用蓝色车替换他的红色车。你只需要更多,如果

  1. 您的模拟中有没有车主的汽车
  2. 您需要能够区分一辆无主的红色汽车和另一辆。

在一个简单的模型中,1 可能是真的,2 不是。在这种情况下,所有无主的红色汽车都是同一辆红色汽车,那么为什么还要让它们与众不同呢?

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

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

在每种情况下,哪些信息是重要的?发射器身份在引爆时真的很重要吗?为什么?敌人会不会发动报复性打击?不?那么这对于爆炸来说不是重要的信息。发布后可能甚至都不是重要信息。发射可以简单地是一个步骤,其中属于发射器 A 的导弹数量减少,而飞行中的导弹数量增加。

现在,您可能对这些问题有了一个很好的答案,但您应该先完全映射您的模型,然后再开始使用它们可能不需要的身份来处理对象。

于 2013-10-07T17:00:02.473 回答
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获取更多信息。

于 2013-10-07T16:24:01.483 回答
1

In Haskell the concepts of values and identities are decoupled. All variables are simply immutable bindings to values.

There are a few types whose value is a mutable reference to another value, such as IORef, MVar and TVar, these can be used as identities.

You can perform identity checks by comparing two MVars and an equality check by comparing their referenced values.

An excellent talk by Rich Hickey goes in detail over the issue: http://www.infoq.com/presentations/Value-Values

于 2013-10-07T17:05:22.093 回答
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, ...}

更新

如果我们想分别使用颜色和ids,我们可以在 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}]
于 2013-10-07T16:27:09.343 回答