Raku中for为何未自动调用Callable混入角色的iterator方法?
解决Perl 6中Callable混入Iterable后无法直接迭代的问题
嘿,我来帮你搞定这个问题!你的代码思路没问题,但Perl 6对Callable(也就是你的&logger这种Block对象)在for循环里有特殊的默认行为,导致即使混入了Iterable角色,也不会自动调用iterator方法。
问题根源
当你写.say for &logger;时,Perl 6会把&logger这个Callable对象当作单元素序列来迭代,而不是把它当作Iterable对象去调用iterator方法。这是因为Perl 6对Callable类型有内置的优先级处理,即使你动态混入了Iterable角色,也会被这个默认行为覆盖。
解决方案
这里有几个实用的办法可以解决这个问题,你可以根据需求选择:
1. 显式调用iterator方法(你已经发现的方法)
这是最直接的方式,明确告诉Perl 6要使用你定义的迭代器:
.say for &logger.iterator;
2. 直接调用Callable获取可迭代的Hash
你的logger在无参数时会返回存储事件的%store(Hash本身就是可迭代的),所以可以直接调用它来迭代:
# 两种写法都可以 .say for &logger(); .say for |&logger; # 使用|展开Callable的返回值
3. 用自定义类包装Callable
如果你希望保持&logger作为对象本身可迭代,可以把它包装到一个实现了Iterable的类里:
class CallableIterable does Iterable { has &.callable; method iterator(--> Iterator:D) { &!callable(Nil).iterator; } } my &logger = -> $event { state %store; if ($event) { %store{DateTime.new(now)} = $event; } else { %store; } } logger("One"); logger("Two"); my $iterable-logger = CallableIterable.new(callable => &logger); .say for $iterable-logger; # 现在可以直接迭代了
4. 强制转换为Iterable类型
通过as关键字强制Perl 6把&logger当作Iterable对象处理,这样就会调用你的iterator方法:
.say for &logger as Iterable;
补充说明
你之前尝试调整role的定义(比如移除does Iterable或者同时混入Iterable和Forable)没有效果,核心原因还是Perl 6对Callable的默认迭代行为,和role的定义关系不大。只要让Perl 6绕过对Callable的特殊处理,就能正常使用你定义的迭代逻辑啦。
内容的提问来源于stack exchange,提问作者jjmerelo
相关产品推荐
相关产品推荐

