无限形容的是那些一直在增加的,没有上限的东西,比如服务器要处理的网络连接、消息代理要处理的事件、游戏控制台要接收的玩家指令。Elixir用流类型表示可能不会结束的数据。相应的Stream模块则提供了许多高阶函数,用于操作和创建数据流。本节将讲解如何处理无限数据流。
我们可以用range字面量创建最简单的流,在IEx中试试:
range是延迟集合。延迟集合仅在必要时才执行。我们的range值仅包含从1到10的计数指令。它没有将所有数都放在内存里。从1到10的range值与从1到10亿的range值占用的内存空间是相同的。如果想查看所有数字,我们需要访问它们的操作。例如,可以用Enum.each/2遍历所有数字:
函数each/2逐个访问集合中的数字。让我们再看一个更难的例子。第4章曾用递归函数构建阶乘模块。现在我们用流实现一个不同的阶乘算法。先假设我们的范围是从1到1000万。让我们构建新的Factorial模块:
在IEx中试试:
这是另一种解决阶乘问题的方式。这次,我们没有用递归函数乘以递减的数字,而是借助集合解决问题。我们可以将整数看作一个集合,从中取n个数,然后从小到大将它们与累积值相乘。这个算法的速度非常快!我们使用了流,因为它是延迟集合,所以1000万个数字不会立马求值。
我们的代码工作正常,但它还有局限性。它的上限是1000万。我们希望它是无上限的。在Elixir中,可以使用高阶函数Stream.iterate/2表示不停扩展的集合。它会创建一个动态流。只有当我们访问集合中某一项时,动态流才会确定下一项。该函数需要一个起始值和一个递增函数。递增函数接收前一个值,而我们要决定怎样计算下一个值。让我们看看它如何工作:
我们创建了一个无限的数字数据流,每次迭代数字都会加一。这里我们只取前五个数字,但我们可以让它永远运行下去:(www.xing528.com)
现在让我们改进阶乘函数,让它适用于无限的数字:
现在它适用于任何数字,在IEx中试试:
Elixir还有其他函数可以生成无限集合,例如,Stream.cycle/1。cycle函数可以轻松创建循环遍历若干项的无限集合。比方说,我们要给万圣节来我们家的孩子送糖果。我们希望把巧克力、果冻、薄荷糖平均分配出去。换句话说,给第一个孩子一粒巧克力,给第二个孩子一颗果冻,给第三个孩子一颗薄荷糖,给第四个孩子一粒巧克力,如此循环。让我们创建Halloween模块:
在讲解细节之前,让我们在IEx中试试我们的代码:
函数返回一个元组列表,每个元组代表一个孩子及其得到的糖果。Stream.cycle/1函数送出薄荷后,会返回列表开头再次开始送巧克力。
~w是单词列表的魔符。Enum.zip/2函数通过组合两个列表来创建新列表,其中新列表的元素顺序与原始列表的相同。每个新元素都是一个元组,该元组包含每个列表中的一项。其中一个列表是无限的,它在巧克力、果冻、薄荷之间循环,另一个列表是有限的,记录着孩子们的名字。函数的终止条件是每个孩子都得到一颗糖果。如果想创建一个延迟组合,我们也可以使用Stream.zip/2。
延迟计算允许我们使用无限集合,并为我们提供了创建新解决方案的新可能性。Stream模块的高阶函数,可帮助开发人员轻松处理延迟集合。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。