10

我已经做了几年面向对象的编程,但我没有做过太多的函数式编程。我对飞行模拟器很感兴趣,并对 Lisp 的函数式编程方面感到好奇。飞行模拟器或任何其他现实世界的模拟器在面向对象的范式中对我来说很有意义。

以下是我的问题:

面向对象是代表现实世界模拟领域的最佳方式吗?

我知道 Common Lisp 有 CLOS(lisp 的 OO),但我的问题实际上是关于用函数式语言编写飞行模拟器。那么如果你打算用 Lisp 来写,你会选择使用 CLOS 还是用函数式的方式来写呢?

有人对用 lisp 或任何功能语言编写飞行模拟器有任何想法吗?

2012 年 11 月 8 日更新 - 对那些感兴趣的人有一个类似的 SO 问题 ->函数式编程如何应用于模拟?

4

7 回答 7

12

将“Lisp”视为一种函数式语言是一个常见的错误。确实,最好将其视为一个语言家族,但现在人们说 Lisp 时,他们通常指的是 Common Lisp。

Common Lisp 允许函数式编程,但它本身并不是函数式语言。相反,它是一种通用语言。Scheme 是一个小得多的变体,在方向上更具功能性,当然还有其他变体。

至于你的问题,这是一个好的选择吗?这真的取决于你的计划。Common Lisp 在这类事情上特别有一些真正的优势。它在您通常在所谓的脚本语言中看到的级别上既具有交互性又具有内省性,使其开发速度非常快。同时它已编译并具有高效的编译器,因此您可以期待与其他高效编译器相同的性能(典型的 ime 是 c 的两倍)。虽然是一门大型语言,但它的设计比 c++ 之类的东西要一致得多,而且元编程功能可以为您的特定应用程序编写非常干净、易于理解的代码。如果你只看这些方面,普通的 lisp 看起来很神奇。

但是,也有缺点。社区很小,如果您正在寻找帮助,您将找不到很多人提供帮助。虽然内置库很大,但您不会找到那么多的 3rd 方库,因此您最终可能会从头开始编写更多的库。最后,虽然它绝不是一个有围墙的花园,但 CL 并没有像 python 那样与外部库的平滑集成。这并不意味着你不能调用 c 代码,有很好的工具可以做到这一点。

顺便说一句,CLOS 是我能想到的最强大的 OO 系统,但如果你来自主流的 c++/java/c#/etc,它是完全不同的方法。OO 背景(是的,它们不同,但除了单一与多重 inh 之外。没有那么多)一开始你可能会觉得它有点奇怪,几乎翻了个底朝天。

如果你走这条路,如果你自己用 CLOS 编写,你将不得不注意实际渲染管道的性能问题。类系统具有令人难以置信的运行时灵活性(即在运行时更新类定义不是通过猴子补丁等,而是通过实际更改类和更新实例)但是您为此付出了一些调度成本。

对于它的价值,我过去曾使用 CL 来研究需要数值效率的代码,即不同类型的模拟。这对我来说很有用。在那种情况下,我并不担心使用现有代码——它不存在,所以无论如何我都是从头开始编写几乎所有东西。

总之,它可能是这个项目的一个很好的语言选择,但不是唯一的。如果您不使用兼具高级方面和良好性能的语言(如 CL、OCaml 和其他一些语言),我肯定会考虑使用 lua 之类的语言或也许使用两级方法的可能性python(很多库)在一些 c 或 c++ 代码之上完成繁重的工作。

于 2009-04-05T14:32:44.643 回答
7

如果您查看游戏或模拟器行业,您会发现很多C++以及一些添加的脚本组件。也可以有用其他语言编写的用于风景设计或相关任务的工具。但是在该领域中使用的 Lisp 很少。你需要成为一名优秀的黑客才能从 Lisp 中获得必要的性能,并能够访问或编写低级代码。你如何获得这个诀窍?尝试,失败,学习,尝试,减少失败,学习,......除了编写代码并进行试验之外别无他法。Lisp 对于优秀的软件工程师或那些有潜力成为优秀软件工程师的人非常有用。

主要障碍之一是垃圾收集器。要么你有一个非常简单的(然后你有随机暂停的性能问题),要么你有一个复杂的(然后你有一个问题让它正常工作)。只有少数适​​合的垃圾收集器存在——大多数 Lisp 实现都有很好的 GC 实现,但仍然没有针对实时或接近实时的使用进行调整。例外情况确实存在。使用 C++,您可以忘记 GC,因为通常没有。

使用垃圾收集器进行自动内存管理的另一种替代方法是不使用 GC 并“手动”管理内存。这被一些需要支持实时响应的(甚至是商业的)Lisp 应用程序使用(例如过程控制专家系统)。

在该地区开发的最近的东西是Crash Bandicoot来自顽皮狗的 Playstation I(后来的游戏是 Playstation II)的游戏(以及后来的游戏)。由于它们已被索尼收购,因此他们为 Playstation III 改用 C++。他们的开发环境是用 Allegro Common Lisp 编写的,它包括一个用于 Scheme(一种 Lisp 方言)变体的编译器。在开发系统上,代码被编译,然后在开发过程中下载到 Playstation。他们有自己的 3d 引擎(非常令人印象深刻,总是得到游戏杂志的好评)、增量关卡加载、针对许多不同演员的复杂行为控制等。所以 Playstation 确实在执行 Scheme 代码,但内存管理没有完成通过 GC(afaik)。他们必须自己开发所有技术——没有人提供基于 Lisp 的工具——但他们可以,因为他们是优秀的软件开发人员。从那以后,我再也没有听说过类似的项目。请注意,这不仅仅是用于脚本的 Lisp——它一直是 Lisp。

在 Scheme 方面,还有一个新的有趣的实现,称为Ypsilon Scheme。它是为弹球游戏开发的——这也可能是其他游戏的基础。

在 Common Lisp 方面,有 Lisp 应用程序与飞行模拟器对话并控制它们的各个方面。有一些基于 SDL 的游戏库。有 OpenGL 的接口。还有类似“开放代理引擎”的东西。还有一些用 Common Lisp 编写的 3d 图形应用程序——甚至是一些复杂的应用程序。但在飞行模拟领域,现有技术很少。

关于 CLOS 与函数式编程的话题。可能一个都不会使用。如果您需要从系统中挤出所有可能的性能,那么 CLOS 已经有一些人们可能希望避免的开销。

于 2009-04-05T15:49:26.253 回答
6

看看函数响应式编程。Haskell 中有很多框架(不知道其他语言),其中大部分是基于箭头的。基本思想是表示随时间变化的值和事件之间的关系。因此,例如,您将编写(使用没有特定库的 Haskell 箭头符号):

   velocity <- {some expression of airspeed, heading, gravity etc.}
   position <- integrate <- velocity

第二行声明了位置和速度之间的关系。<- 箭头操作符是将所有东西联系在一起的一堆库调用的语法糖。

然后稍后你可能会说:

   groundLevel <- getGroundLevel <- position
   altitude <- getAltitude <- position
   crashed <- liftA2 (<) altitude groundLevel

声明如果您的高度低于您所在位置的地面水平,那么您已经坠毁。就像这里的其他变量一样,“崩溃”不仅仅是一个值,它是一个随时间变化的值流。这就是为什么使用“liftA2”函数将比较运算符从简单值“提升”到流的原因。

IO 在这种范式中不是问题。输入是时变值,例如操纵杆 X 和 Y,而屏幕上的图像只是另一个时变值。在最顶层,您的整个模拟器是从输入到输出的箭头。然后调用“运行”函数,将箭头转换为运行游戏的 IO 动作。

如果你用 Lisp 编写这个,你可能会发现自己创建了一堆基本上重新发明箭头的宏,所以从一开始就找出箭头可能是值得的。

于 2009-04-07T17:35:43.763 回答
2

我对飞行模拟一无所知,你也没有特别列出它们包含的任何内容,所以这主要是关于用 Lisp 编写 FS 的猜测。

为什么不:

  • Lisp 擅长探索性编程。我认为,由于 FS 存在这么久,并且有免费和开源的示例,它不会从这种类型的编程中受益。

  • 飞行模拟游戏大多(我猜)是用静态的、本地编译的语言编写的。如果您正在寻找纯粹的运行时性能,那么在 Lisp 中,这往往意味着类型声明和其他不太 Lispy 的构造。如果你用幼稚的方法没有得到你想要的性能,你优化的 Lisp 最终可能看起来很像 C,而 Lisp 在编写 C 方面不如 C。

  • 我猜,很多 FS 都在连接像 OpenGL 这样的图形库,它是用 C 编写的。根据您的 FFI / OpenGL 绑定的方式,这可能再次使您的代码看起来像 C-in-口齿不清。你可能没有 Lisp 在 Web 应用程序(它包括生成纯文本的树结构,这是 Lisp 擅长的)中取得的巨大胜利。

为什么:

  • 我看了一眼 FlightGear 源代码,我看到了很多结构样板——即使是直端口也可能最终只有一半大小。

  • 他们到处使用字符串作为键(C++ 没有符号)。他们将 XML 用于半人类可读的配置文件(C++ 没有运行时阅读器)。在这里简单地切换到本机 Lisp 结构可能会以最小的努力获得巨大的胜利。

  • 没有什么看起来很复杂,即使是“人工智能”。这只是保持一切井井有条的问题,而 Lisp 会在这方面做得很好,因为它会更短。

但是关于 Lisp 的巧妙之处在于它是多范式的。您可以使用 OO 来组织“对象”,并使用 FP 在每个对象内进行计算。我说只是开始写作,看看它会把你带到哪里。

于 2009-04-06T17:21:52.357 回答
1

我不会说函数式编程特别适合飞行模拟。一般来说,函数式语言对于编写科学模拟非常有用,尽管这是一个稍微特殊的案例。真的,你可能会更好地使用标准的命令式(最好是 OOP)语言,比如 C++/C#/Java,因为它们往往拥有更好的物理库和图形 API,这两者你都需要非常使用沉重。此外,OOP 方法可能更容易表示您的环境。要考虑的另一点是(据我所知)当今市场上流行的飞行模拟器几乎完全是用 C++ 编写的。

本质上,我的理念是,如果没有特别好的理由需要使用函数式范例,那么就不要使用函数式语言(尽管没有什么可以阻止你在 OOP/混合语言中使用函数式构造)。我怀疑使用经过良好测试的 C++ API 和更常与游戏开发相关的语言(与飞行模拟有许多共同点)进行开发过程的痛苦会少得多。现在,如果您想在模拟器中添加一些复杂的 AI,Lisp 似乎是一个更明显的选择,尽管即使那样我也不会跳入其中。最后,如果您真的热衷于使用函数式语言,我建议您使用更通用的语言之一,例如 Python 甚至 F#(实际上都是混合的命令式函数式语言),

于 2009-04-05T14:20:25.390 回答
1

我首先会想到模拟的性质。

一些模拟需要像飞行模拟器一样的交互。我不认为函数式编程对于交互式(阅读:CPU 密集型/响应关键型)应用程序可能是一个不错的选择。当然,如果您可以访问 8 个与 Linux 连接在一起的 PS3,您就不会太在意性能。

对于像进化/遗传编程这样的模拟,你设置它并让'er rip,功能性语言可能比OO语言更好地帮助建模问题域。并不是说我是函数式编程方面的专家,但在我看来,递归编码的简便性和函数式语言中常见的惰性求值的想法似乎非常适合“让她撕开”类型的模拟人生。

于 2009-04-07T17:48:51.330 回答
0

函数式语言存在一些问题,那就是它们不能很好地与状态相结合,但它们确实与过程相得益彰。所以在某种程度上可以说它们是面向行动的。这意味着您将浪费时间模拟飞机,您要做的是模拟飞行飞机的动作。一旦你冷静下来,你可能会让它发挥作用。

现在顺便说一句,恕我直言,haskell 不是很好,因为它对于“游戏”来说太抽象了,这种应用程序都是关于输入/输出的,但 Haskell 是关于避免 IO,所以它会成为一个 monad 的噩梦,你会反对这种语言。Lisp 是一个更好的选择,或者 Lua 或 Javascript,它们也是函数式的,但不是纯粹的函数式,所以你可以试试 Lisp。无论如何,在任何这些语言中,您的图形都将是 C 或 C++。

然而,一个严重的问题是文档很少,关于函数式语言和“游戏”的教程也很少,当然科学模拟在学术上是有记录的,但这些论文非常密集,如果你成功了,也许你可以写下你的经验,因为它是其他人现在是一个相当空旷的领域

于 2009-04-05T14:36:23.290 回答