http://www.perlmonks.org?node_id=11101451

Athanasius has asked for the wisdom of the Perl Monks concerning the following question:

I only started learning Perl 6 within the last week, so please bear with me. :-)

I had a script working nicely, then I added another statement (line D in the code below) and received a run-time error message like this:

The iterator of this Seq is already in use/consumed by another Seq (you might solve this by adding .cache on usages of the Seq, or by assigning the Seq into an array)

From the documentation I gather that I have somehow created a Seq object and then attempted to iterate it a second time. But I don’t understand where or how the Seq object is being created and used.

I managed to pare down the code to the following SSCCE:

use v6; my @paths = data(); my @dirs.push: $_.split('/') for @paths; # A # @dirs.perl; # B say $_.join('/') for @dirs; # C my $depth = @dirs.map(*.elems).min; # D sub data { my $text = q:to/END/; /aardvark/bison/camel/dromedary /aardvark/bison/camel/dromedary/elephant END return $text.lines; }

Lines A, C, and D are all required to produce the error. Uncommenting line B removes the error (by caching, apparently, although I’m not sure exactly what is cached). So I have 3 questions:

  1. Where are the Seq object(s) being created and used?
  2. I tried examining the contents of @dirs using say @dirs.perl;, but that had the same effect as uncommenting line B. Is there a way to examine the contents of @dirs (like using Data::Dump in Perl5) without introducing changes?
  3. Adding line B removes the error, but feels like a kludge. What is the correct way of writing this sort of code?

In case it matters:

11:03 >perl6 -v This is Rakudo Star version 2019.03.1 built on MoarVM version 2019.03 implementing Perl 6.d. 12:14 >

Thanks,

Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Replies are listed 'Best First'.
Re: [Perl6] Seq iterator already consumed
by choroba (Cardinal) on Jun 17, 2019 at 09:11 UTC
    From the documentation of lines it seems that it returns a Seq object.

    But

    say @dirs.^name;

    returns Array, so it should be OK. But: split returns a Seq, too! Behold:

    say @dirs[0].^name; # Seq

    That's probably the problem.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

      Thanks, choroba. I didn’t know about .^name — that’s useful. I found this explanation under https://docs.perl6.org/language/classtut#Introspection:

      The syntax of calling a method with .^ instead of a single dot means that it is actually a method call on its meta class, ...

      I’ll have to look into that.

      But

      say @dirs.^name;

      returns Array, so it should be OK.

      Actually, at this point @dirs contains two Seq objects. I think the subsequent call to split iterates each Seq (which is ok, since a Seq is a one-shot iterable); the later call to map attempts to iterate them again, causing the error.

      This explanation would be fine if it weren’t for line C. Since join iterates over the Seq, it should be line C that produces the error. But it isn’t — and, as I stated in the OP, removing line C removes the error at line D too. So, still confused. :-(

      But I did find another fix: change line A to this:

      my @dirs.push: $_.split('/').cache for @paths; # A'

      At least this looks less like a kludge than line B.

      Cheers,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        @dirs contains nothing until you call the split. Split doesn't iterate the Seq, it creates them. Then the join iterates them the first time and map tries to iterate them again.

        my @paths = data(); my @dirs.push: $_.split('/') for @paths; # A : @dirs == [(...).Seq, + (...).Seq] say $_.join('/') for @dirs; # C : @dirs == [Seq.new-co +nsumed(), Seq.new-consumed()] my $depth = @dirs.map(*.elems).min; # D : oops, consumed!

        Personally, I think the nicest looking way to fix this is to consume the Seq into an Array (update: though upon consideration, using .cache may be more efficient since it is lazy)

        my @dirs.push: [ $_.split('/') ] for @paths;

        Calling .perl "fixes" the error because the .perl method automatically calls .cache for you. Else attempting to debug via .perl would consume the Seq which would generally break code. The documentation for Seq mentions this:

        Caching is a volatile state exposed to the developer as an optimization. The Seq may become cached by many operations, including calling perl on the Seq (if called prior to a non-cached iteration).

        Good Day,
            Dean

      Totally OT and useless but you got rhythm:

      Medium Uptempo Funky Minor Blues Intro... ...Speak: From the documentation of lines it seems that it returns a s +eq object... Verse (very relaxed) But say at dirs dot caret name Returns array, so it should be OK Colored girls: (very vigorously) Behold! (ad lib: Oh yeah, Oh yeah!) But colon split returns a seq, too! Colored girls (very theatrical): Sequence! Say at dirs square bracket zero square bracket dot caret name Colored girls (very lyrically): That's probably the problem. Fill in and move on…

      «The Crux of the Biscuit is the Apostrophe»

      perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help