Clojure:何时使用可变状态
我正在 Clojure 中实现一个小“游戏”东西。到目前为止,我正在函数之间传递“世界状态”对象。它非常“实用”,我可以通过简单地向系统提供一个虚构的世界状态来模拟游戏的任何时刻。
由于 Clojure 有一个非常复杂的系统来管理状态(引用、原子...),我想知道更惯用的 Clojure 编程方式是什么,是使用它的系统还是坚持使用更实用的方法。
谢谢。
编辑:
I'm implementing a litte "game" thing in Clojure. So far I'm passing around a "world status" object among functions. It is very "functional" and I can simulate any moment of the game by simply feeding the system a made-up world state.
Since Clojure has a very sophisticate system for managing state (references, atoms...) I wanted to know what is the more idiomatic way of programming Clojure, whether to use its system or to stick to a more functional approach.
Thanks.
EDIT:
This is an interesting read I've just found describing more or less the pattern I'm using.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
纯函数已经是 Clojure 的惯用法。引用类型(ref、atom、agent)用于协调共享状态。
只要没有共享任何内容(您不在一个线程中更新世界,同时在另一个线程中渲染,或者在自己的线程上协调多个玩家),就没有理由共享状态,因此没有理由打破纯功能风格。
另一个例外是优化性能:但是当性能可以通过可变性来增强时,您希望使突变尽可能保持本地化。这就是瞬态或 Java 数组的用武之地。包装这些可变优化的函数通常仍然是纯函数。
Pure functions are already idiomatic Clojure. The reference types (ref, atom, agent) are there to coordinate shared state.
As long as nothing is shared (you aren't updating the world in one thread while rendering in another, or coordinating multiple players on their own threads) there is no reason to share state, and thus no reason to break from purely-functional style.
Another exception would be to optimize for performance: but when performance can be enhanced by mutability you want to keep the mutations as local as possible. This is where transients or Java arrays come in. The functions that wrap these mutable optimizations are usually still pure functions.
Clojure 是一种函数式编程语言,旨在利用多核/SMP 处理器。无需共享内存访问,您就可以从函数式编程语言中获益匪浅,Erlang 确实在这方面做得非常出色,但它并没有充分利用所有处理器的能力。
与“Actor 模型”语言相比,clojure 的亮点在于多个线程希望以有意义且协调的方式处理相同的数据。如果您遇到像图像处理这样的可并行化问题,那么您不需要这些优势,您只需向每个工作人员发送一份数据即可。当这些数据位相互依赖时,协调共享访问就成为真正的优势。
在游戏环境中,您可以使用它让多个线程更新游戏世界,并用一个线程向用户显示它。
ref
将确保用户始终看到一致的游戏世界,而许多线程正在编辑它。如果没有这个,您将需要让每个线程负责确定何时显示它给用户,或者只在线程上。除了速度之外,使用这种模型编辑游戏世界的另一个原因是允许您将执行不同操作的进程分离到不同的线程,在 Rich 的早期 clojure 示例之一中,他展示了一个蚂蚁模拟器每只蚂蚁都有自己的线程来更新蚂蚁在板上的位置,这使得一些非常简短的代码成为可能。
Clojure is a functional programming language designed to take advantage of multi-core/SMP processors. You can get a lot out of a functional programming language without shared memory access, and indeed Erlang does this wonderfully, but it does not take advantage of all the processors powers.
Where clojure shines in comparison to 'Actor model' languages is when multiple threads want to work on the same data in a meaningful and coordinated way. If you have a trivially parallelizable problem like image processing then you don't need these advantages and you can just send one chunk of data to each worker. When these bits of data are interdependent then Coordinated shared access becomes a real advantage.
In the context of games you can use this to have many threads updating the game world and one thread showing it to the user. a
ref
will ensure that the user always sees a consistent game world, while many threads are editing it. without this you would need to have each thread responsible for determining when to show it to the user, or only have on thread.Another reason to use such a model for editing a game world, other than speed, is to allow you to seperate processes that do different things into different threads in one of Rich's early clojure examples he shows an ant simulator where each ant has its own thread that updated the ants position on the board, which made for some very short and simple code.
只要只有一个线程或上下文正在写入该数据(这就是重复时发生的情况),以当前的功能风格编写游戏就已经是正确的方法。实际上,您提到的这些设施的关键优势是在处理共享状态时。如果您想通过多线程并行化您的程序,那么利用这些工具可能会很有用。
Writing the game in the current functional style is already the right way, so long as only one thread or context is writing to that data (which is what happens when you recur). Really, the key advantage to those facilities you mentioned is when dealing with shared state. If you want to parallelize your program with multithreading, then leveraging those tools might be useful.