使用Iterable和Iterator角色实现可迭代的类

uzl*_*xxx 5 iterator perl6 raku

假设我们有以下组成角色的类Iterable

class Word-Char does Iterable {
    has @.words;

    method !pairize($item) {
        return $item => $item.chars;
    }

    method iterator( Word-Char:D: ) {
        @!words.map({self!pairize($_)}).rotor(1).iterator
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以Positional在对象构造期间将对象分配给变量,然后对该变量进行迭代:

my @words = Word-Char.new: words => <the sky is blue>;
.say for @words;
Run Code Online (Sandbox Code Playgroud)

输出:

(the => 3)
(sky => 3)
(is  => 2)
(blue => 4)
Run Code Online (Sandbox Code Playgroud)

但是,如果要传递对象该怎么办?我如何确保它仍然可迭代?:

my $w = Word-Char.new: words => <the sky is blue>;
sub f( $w ) {
    .say for $w
}
f($w);
Run Code Online (Sandbox Code Playgroud)

输出

Word-Char.new(words => ["the", "sky", "is", "blue"])
Run Code Online (Sandbox Code Playgroud)

目标

通过使用IterableIterator或两者兼而有之,我想,如果可能的话,要能够遍历任何地方实现这些角色的类的实例对象。现在我知道,通过在对象构造过程中将实例对象分配给Positional变量,我可以获得类提供的可迭代项,但这不是我想要的。相反,我想传递对象本身,并在我认为有需要的地方/地方对其进行遍历。

Xli*_*iff 6

在处理具有迭代器角色的标量值时,完成您要尝试的操作的最简单方法是告诉perl6您的标量值是可迭代的。您可以通过将其后缀来实现[]。您的示例如下所示:

my $w = Word-Char.new: words => <the sky is blue>;
.say for $w[]
Run Code Online (Sandbox Code Playgroud)

另一件事....

您的迭代代码存在一个错误,即它不会在返回之前重置自身IterationEnd。快速修复如下所示:

class Word-Char does Iterable does Iterator {
    has @.words;
    has Int $!index = 0;

    method !pairize($item) {
        return $item => $item.chars;
    }

    method iterator() {self}
    method pull-one( --> Mu ) {
        if $!index < @!words.elems {
            my $item = @!words[$!index];
            $!index += 1;
            return self!pairize($item);
        }
        else {
            $!index = 0;
            return IterationEnd;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这意味着您必须将所有迭代逻辑(及其属性)保留在主类中。另一种方法是使用匿名类,而不是使用self

class Word-Char does Iterable {
    has @.words;

    method !pairize($item) {
        return $item => $item.chars;
    }

    method iterator() {
        my @words = @!words;

        class :: does Iterator {
            has $.index is rw = 0;

            method pull-one {
              return IterationEnd if $!index >= @words.elems;
              @words[$!index++];
            }
        }.new;
    } 
}
Run Code Online (Sandbox Code Playgroud)

上面的优点是您可以使迭代逻辑更整洁,并与对象的其余部分隔离。您也不必担心重置状态。