本节将学习如何将管道运算符与数据流组合起来使用,创建一组使用数据流的管道任务。我们有两种策略:急切策略和延迟策略。急切策略是指,所有元素都完成一轮计算后,才能将它们送到下一个阶段。延迟策略是指,部分元素完成一轮计算后,就将它们送到下一个阶段。两者的区别在于,前者只有在处理完所有元素后,才能输出结果;后者只处理少量元素,就能开始输出结果。此前,我们大量使用了急切策略。本节将探讨延迟策略的优点。
为了更好地理解急切策略和延迟策略,不妨想象一套机械装配线,它由几台加工机器组成,原件必须依次经过这些加工机器的处理才能完成装配。急切策略是指,必须等所有原件都完成第一步加工后,才能进入下一步。如果有人在装配线末端等待结果,他要等很长时间才能看到成品。延迟策略是指,不必等所有原件都完成第一步加工才进入下一步。假如一共有三个原件,只要第一个原件完成第一步加工,它就会进入下一步,而不必等待其他两个原件都完成第一步加工。
让我们构建一个ScrewsFactory模块来模拟螺钉的加工过程。这个模块将接收金属件,给它们刻上螺纹,加上螺钉头。我们让每一步流程都等待几毫秒来模拟需要时间才能完成的步骤。模拟加工时间可以更好地反映制作大量螺钉的耗时情况。将模块写入screws_factory.ex文件中:
在讨论代码细节之前,现在IEx中试试:
制造螺丝的过程非常慢,因为我们采用的是急切策略。它首先给所有金属件刻上螺纹,然后再给它们添加螺丝头,最后才在管道的末端显示所有螺钉成品。当我们要对集合中的所有元素进行缓慢的操作时(例如,访问外部数据库或调用REST API),这种策略会导致性能下降。只处理几个元素还好,如果要处理数千个元素,就慢得难以忍受了。
如果不追求速度,急切策略可以解决大多数问题。但是,在加工螺钉的问题上,我们还需要提高效率。
让我们用数据流改写run/1函数,从而采用延迟策略。
再试试看:
现在我们提高了对每个原件的调配效率!令人惊奇的是,我们不需要修改内部功能,而只需要修改管道。除了改用Stream.map/1创建数据流,其他地方几乎没有变化。(www.xing528.com)
现在假设公司高管对我们的改进很满意,他们特意购买了新的设备。现在,攻丝机可以同时处理50个原件,而镦锻机可以同时处理100个原件。
让我们修改代码以充分利用新机器:
试试看它的加工速度有多快:
超级快!让我们看看它是如何做到的,首先看run/1函数。这里的新函数是Stream.chunk/2和Stream.flat_map/2。chuck函数负责累积原件,然后将它们送往下一个函数。它在处理管道中创建一个队列。当队列满或流结束时,它会将累积的原件发送给下一个函数。单独理解可能更容易:
分解步骤使用的是Enum,但Stream版本的运行方式完全相同。我们创建了一个列表,它是由包含两个累积项的小列表组成的。然后是flat_map,它返回一个新列表,其中包含完成某步加工的原件。
让我们看看它单独执行的效果:
然后,我们可以再次累积原件,准备送往下一个函数。批量加工原件提高了整体效率。因为现在加工一个原件和加工一堆原件的时间成本几乎相同。
Stream的高阶函数和延迟策略可以用来创建更高效的程序。如果你的管道任务中有些操作耗时较长,而你又不希望让客户在管道末端等待,就应该考虑采用延迟策略。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。