不可变的游戏对象,基本的函数式编程问题

jdo*_*oig 8 functional-programming immutability

我正在尝试"学习更多"和"从函数式编程中吸取教训"以及不可变性对并发性有益的想法等.

作为一个思想练习,我想象一个简单的游戏,Mario-esq类型角色可以跑来跑去向他射击的敌人......

然后我试着想象这是使用不可变对象在功能上编写的.

这引发了一些困扰我的问题(成为一个势在必行的OO程序员).

1)如果我的小家伙在位置x10,y100向右移动1个单位,我只是使用他的旧值重新实例化他的x位置+1(例如x11,y100)?

2)(如果我的第一个假设是正确的)如果我的输入线程移动小家伙右1单位和我的敌人AI线程射击小家伙和敌人-ai线程解决输入线程然后我的家伙将失去健康,然后输入线程解决,获得它并向右移动......

这是否意味着我无法解雇 - 而且 - 即使在不变的情况下也会忘记我的线索?当我得到两个线程操作的结果时,我是否需要发送我的线程来执行他们的操作然后新的()同步的小家伙?还是有简单的"功能性"解决方案?

这与我日常所面临的线程问题略有不同.通常我必须决定我是否关心线程解决的顺序.在上述情况下,我在技术上并不关心他是否先受到伤害或移动.但我确实关心实例化期间的竞争条件会导致一个线程数据完全丢失.

3)(再次,如果我的第一个假设是正确的)不断实例化一个对象的新实例(例如马里奥家伙)有一个可怕的开销,使其成为一个非常严重/重要的设计决策?

编辑 对不起这个额外的编辑,我不是关于跟进问题的好习惯...

4)如果不变性是我应该努力的事情,甚至跳过实例化已经改变的对象的新版本的箍......如果我每次移动时实例化我的人(只有不同​​的位置)我不是如果他是可变的话我会遇到同样的问题吗?在某个时刻引用他的东西实际上是在看旧值吗?我越深入了解我的头脑越多,因为生成具有不同值的相同事物的新版本似乎是可变性的,通过黑客攻击.:¬?

我想我的问题是:这应该怎么做?如何改变他的立场有什么好处呢?

for(ever)//simplified game-loop update or "tick" method
{
   if(Keyboard.IsDown(Key.Right)
      guy = new Guy(guy){location = new Point(guy.Location.x +1, guy.Location.y)};
}
Run Code Online (Sandbox Code Playgroud)

令人困惑的是:上面的代码意味着那个人是可变的!(即使他的属性不是)

4.5)这完全是一成不变的人吗?

谢谢,

J.

Mau*_*Mau 2

  1. 如果我的小家伙在 x10,y100 位置向右移动 1 个单位,我是否只需使用他的旧值重新实例化他,并将他的 x 位置+1(例如 x11,y100)?

嗯,不一定。你可以实例化这个家伙一次,并在游戏过程中改变它的位置。您可以与代理一起对此进行建模。这家伙是代理,AI 也是,渲染线程也是,用户也是。

当人工智能射击该人时,它会向其发送一条消息,当用户按下箭头键时,它会发送另一条消息,等等。

 let guyAgent (guy, position, health) =
     let messages = receiveMessages()
     let (newPosition, newHealth) = process(messages)
     sendMessage(renderer, (guy, newPosition, newHealth))
     guyAgent (guy, newPosition, newHealth)
Run Code Online (Sandbox Code Playgroud)

“一切”现在都是不可变的(实际上,在代理的双补丁队列的幕后可能确实有一些可变的状态)。

  1. 如果不变性是我应该努力追求的,甚至跳过实例化已更改的对象的新版本的麻烦……如果我在我的家伙每次移动时都实例化他(仅以不同的位置),我不是有完全相同的东西吗?如果他反复无常,我也会遇到问题吗?

嗯,是。使用可变值循环和使用不可变值重复是等效的。

编辑:

  1. 对于代理商来说,维基百科总是有帮助的。
  2. Luca Bolognese 有一个代理的F# 实现
  3. 这本书(有些人称之为《智能代理书》)虽然针对的是人工智能应用(而不是软件工程的角度),但它非常出色。