有人在Perl中为懒惰评估的列表找到了一个很好的解决方案吗?我已经尝试了很多方法来改变类似的东西
for my $item ( map { ... } @list ) { }
例如,通过绑定@list进入懒惰的评估.我试图避免分解和编写源过滤器来执行此操作,因为它们会影响您调试代码的能力.有没有人取得任何成功.或者你只需要分解并使用while循环?
注意:我想我应该提一下,我有点迷上了有时用于功能转换列表的长grep-map链.所以它不是foreach循环或while循环.这就是地图表达式倾向于将更多功能打包到同一垂直空间中.
如前所述,for(每个)是一个急切的循环,所以它想在开始之前评估整个列表.
为简单起见,我建议使用迭代器对象或闭包,而不是尝试使用延迟评估的数组.虽然你可以使用平局来评估一个懒惰的无限列表,但是如果你曾经(直接或间接地,如上面的foreach)要求整个列表(甚至整个列表的大小),你可能会遇到麻烦.
无需编写完整的类或使用任何模块,只需使用闭包即可创建一个简单的迭代器工厂:
sub make_iterator { my ($value, $max, $step) = @_; return sub { return if $value > $max; # Return undef when we overflow max. my $current = $value; $value += $step; # Increment value for next call. return $current; # Return current iterator value. }; }
然后使用它:
# All the even numbers between 0 - 100. my $evens = make_iterator(0, 100, 2); while (defined( my $x = $evens->() ) ) { print "$x\n"; }
CPAN上还有Tie :: Array :: Lazy模块,它为懒惰数组提供了更丰富,更丰富的接口.我自己没有使用过这个模块,所以你的里程可能会有所不同.
祝一切顺利,
保罗
[旁注:请注意,地图/ grep链中的每个步骤都非常渴望.如果你同时给它一个大的清单,你的问题比决赛更早开始foreach
.]
你可以做些什么来避免完全重写是用外循环包装你的循环.而不是写这个:
for my $item ( map { ... } grep { ... } map { ... } @list ) { ... }
......这样写:
while ( my $input = calculcate_next_element() ) { for my $item ( map { ... } grep { ... } map { ... } $input ) { ... } }
这样您就不必显着重写现有代码,只要列表在转换过程中没有增长几个数量级,您就可以获得重写迭代器样式所带来的所有好处.
如果你想制作惰性列表,你必须编写自己的迭代器.一旦你拥有了它,你就可以使用像Object :: Iterate这样的东西,它具有迭代器感知版本的map
和grep
.看一下该模块的源代码:它非常简单,您将看到如何编写自己的迭代器感知子例程.
祝好运, :)
至少有一个特殊情况,其中for和foreach已被优化,不能立即生成整个列表.这就是范围运营商.所以你可以选择说:
for my $i (0..$#list) { my $item = some_function($list[$i]); ... }
并且这将遍历数组,无论你喜欢如何转换,而无需预先创建一长串值.
如果您希望map语句返回可变数量的元素,则可以执行以下操作:
for my $i (0..$#array) { for my $item (some_function($array[$i])) { ... } }
如果你希望比这更普遍的懒惰,那么你最好的选择是学习如何使用闭包来生成惰性列表.MJD的优秀书籍Higher Order Perl可以引导您完成这些技巧.但是请注意,它们将对您的代码进行更大的更改.