Создание последовательности из итератора - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь создать итератор, а затем построить последовательность из него, но он не действует так, как мне кажется. Что случилось?

Вот мой основной класс:

class Foo {
    has $.x = 0;
    has $.max = 3;

    method val() {
        (++$!x > $!max) ?? () !! ($!x, "string $!x")
    }
}

my $foo = Foo.new;
say $foo.val.perl for ^4;

# (1, "string 1")
# (2, "string 2")
# (3, "string 3")
# ()

Он просто повторяется до max, затем возвращает (), работает так, как я думаю.

Затем я создаю из этого итератор, всего один pull-one() метод.

class Foo-Iterator does Iterator {
    has Foo $.foo;

    method pull-one() {
        $!foo.val || IterationEnd
    }
}

my $iter = Foo-Iterator.new(foo => Foo.new);
$iter.pull-one.perl.say for ^4;

# (1, "string 1")
# (2, "string 2")
# (3, "string 3")
# IterationEnd

Это все еще действует так, как я ожидаю.

Если я обращаюсь к нему с помощью Seq, он все равно работает нормально:

.perl.say for Seq.new: Foo-Iterator.new(foo => Foo.new);
# (1, "string 1")
# (2, "string 2")
# (3, "string 3")

Это все еще то, что я ожидаю увидеть, то же самое, что вернул Итератор.

Наконец, я сохраняю Seq в переменной @ и печатаю результаты этого:

my @seq = Seq.new: Foo-Iterator.new(foo => Foo.new);
.perl.say for @seq;
# $(4, "string 1")
# $(4, "string 2")
# $(4, "string 3")

Что с этим? Похоже, что он использует более позднее значение переменной, а не значение, которое она имела во время вызова pull-one () (которое в строке выдает значение). Seq возвращает его как контейнер, а не значение? Это лень в действии, когда он не тянет до тех пор, пока не будет запрошено, чтобы получить более позднее значение?

Если я заставлю val() вернуть +$!x вместо возврата $!x, мне кажется, что оно берет значение и дает мне то, что я хочу, я просто пытаюсь понять поведение, которое вижу.

1 Ответ

0 голосов
/ 16 января 2019

Я сделал ровно 4 изменения в Foo: does Iterator, pull-one, IterationEnd, и я отменяю %!x с <>.

Ваш метод передавал контейнер для $!x когда вы действительно хотите передать значение внутри контейнера, поэтому ему нужно $!x<>.
Причина, по которой вы не заметили это с остальным кодом, заключается в том, что единственное назначение было присвоение массива.eager.

Я сделал остальные изменения, потому что у него уже есть состояние, и оно работает только один раз.Именно так должен работать Iterator.С вашим кодом нет смысла добавлять еще один объект для Iterator, который в основном только переименовывает метод.

class Foo does Iterator {
    #     ^-----------^
    has $.x = 0;
    has $.max = 3;

    method pull-one() {
        #  ^------^

        (++$!x > $!max) ?? IterationEnd !! ($!x<>, "string $!x")
        #                  ^----------^        ^^
    }
}

Теперь, чтобы использовать его

my $seq = Seq.new( Foo.new );

for $seq<> { .say }
# (1 string 1)
# (2 string 2)
# (3 string 3)

my @seq = Seq.new( Foo.new );
for @seq { .say }
# (1 string 1)
# (2 string 2)
# (3 string 3)

Предполагая, чтоваш пример слишком прост, и есть веская причина иметь отдельный Iterator, почему у вас есть мутирующий val метод?

class Foo-Iterator {…}

class Foo does Iterable {
    has $.max = 3;

    method val ( $index ) {
        ($index > $!max) ?? () !! ($index, "string $index")
    }

    method iterator (){
        # pass `self` for method calls or such
        # (could pass internal data additionally/instead)
        Foo-Iterator.new( :foo(self) )
    }
}

class Foo-Iterator does Iterator {
    # the mutating value
    has $!x = 0;

    # make it public only so we don't have
    # to mess with `new` or `BUILD`
    has $.foo is required;

    # only have the mutating logic in this object
    method pull-one() {
        $!foo.val( ++$!x ) || IterationEnd
    }
}

Теперь, чтобы использовать его.

# only one Foo object needed
my $foo = Foo.new;

# for Seq.new($foo.iterator) { .say }
for $foo<> { .say }
# (1 string 1)
# (2 string 2)
# (3 string 3)

for $foo<> { .say }
# (1 string 1)
# (2 string 2)
# (3 string 3)

my $iter-a = $foo.iterator;
my $iter-b = $foo.iterator;

say $iter-a.pull-one;
# (1 string 1)
say $iter-a.pull-one;
# (2 string 2)
say $iter-b.pull-one; # notice that $iter-b isn't tied to $iter-a
# (1 string 1)

my @seq = $foo<>;
for @seq { .say }
# (1 string 1)
# (2 string 2)
# (3 string 3)
...