函数式编程优点
在本系列的第一部分中 ,我讨论了函数式编程的核心概念,并给出了一些例子说明它们如何发挥作用。 函数编程的核心概念列表(再次从第一部分开始)如下:
- 使用功能作为其他功能的输入和输出,
高阶函数 - 使用
map
,filter
和reduce
类型函数而不是循环 - 不变的状态
- 递归代替循环
- 从其他功能组合功能
- 将“纯”功能与具有副作用的功能区分开
我给出了使用前四个项目的一些示例,因此让我们更深入地研究这些项目,并添加第5个项目。
与Elm建立高效的国际象棋AI
但是首先,有关国际象棋编程的一些信息:
为类似象棋的游戏编写AI是一件很有趣的事情。 真正的国际象棋AI必须非常复杂。 电脑下象棋的方式比人类下得多。 他们尝试查看每个可能的移动顺序,并为结果位置分配一个值。 这种蛮力的方法需要大量的工作,以至于如果不进行重大的优化,AI将需要几天的时间才能移动,并且还需要大量的内存。
优化国际象棋AI代码的挑战已经产生了一些很棒的算法,例如alpha-beta修剪和min-max搜索。 击败世界冠军Garry Kasparov的AI Deep Blue内置了专门的“象棋”芯片,使其速度比纯软件解决方案快了许多倍。 此外,国际象棋公开赛百科全书包含了所有流行公开赛的最佳棋步,并且这些棋子作为每个游戏的前十到二十步不需要AI进行工作的数据,都被硬编码到国际象棋程序中。
相比之下,我的宠物项目Wounds(请参阅本系列的第一部分 )的AI非常原始。 在评估董事会中的给定职位时,它将比较军队(包括受伤的部分)的实力,并将其各自的机动性与每个团队可以进行多少法律行动的原始计数进行比较。
我们将看到在不耗尽某些资源(包括人的耐心)的情况下,我们可以展望未来的许多举措。
为简单起见,让我们假设在每个连续回合中合法移动的次数保持不变(不是;它通常随着游戏第一部分中的每个移动而增加)。 第一位在Wounds中是Red的玩家在游戏开始时进行了大约30次合法移动。 AI会尝试将每个动作作为第1层,然后使Blue团队的每个响应(第2层)为30乘以30,即900。您将看到结果。 接下来将考虑Red的27,000次举动(第3页)和Blue的810,000次举动(第4页)。 当您开始看远一点时,很容易耗尽时间和内存。
比较Objective-C和Elm
当我在Objective-C中为此编写代码时,在遇到函数编程之前,我看到递归将使我能够为此编写干净的代码。 当我实现递归代码时,我发现深度为5层时,代码将使我的iPad崩溃。 这样做的原因有两个:一,在董事会上进行下一步行动之前,需要保存每个董事会职位的副本;二,未针对递归对Objective-C进行优化,因此会产生额外的开销。 递归调用(没有优化)会占用堆栈