Printing mathematical series concisely in Raku - sequence

Mathematical series, take for example the consecutive sequence represented here as an array:
my #seq = my $a=0, {++$a} ... *;
for #seq[^10].kv {state $f=0; ($^k < 4 or $^k > 7) ?? say "a$^k = " ~ $^v !! (say "..." if $f ne 1; $f=1) };
Prints:
a0 = 0
a1 = 1
a2 = 2
...
a8 = 8
a9 = 9
1- Is there a simple way to drop just the first element i.e. a0 = 0 from the printed output?
2- Could this code be made more idiomatic?

This might be a bit more idiomatic:
my #seq = 0, *+1 ... *;
say #seq[^4], #seq[7..10]
You don't need to use a lexical variable within the sequence; either Whatever or placeholder variables can safely be used within sequences. Then you can simply select the elements of the sequence you want printed.
Which returns «(0 1 2 3)(7 8 9 10)␤»

You can skip the first N values on any Iterable or Sequence with skip:
for (^5).skip(3) {
.say
}
# 3
# 4
If you don't specify a number, it will skip only one element.

A barebones solution
Let's start with a very simple solution for printing a gist of a sequence. It doesn't deal with the specifics you've added to your question but it's a good starting point:
sub seq-range-gist ( #seq ) {
my #pairs = #seq.pairs;
join "\n", #pairs.head(3)».gist, '...', #pairs.tail(2)».gist
}
Unlike .kv, which converts its invocant into the form key1, value1, key2, value2, key3, value3, ..., i.e. 6 elements if its invocant contains 3 elements, .pairs converts its invocant into the form key1 => value1, key2 => value2, key3 => value3, ....
I used .pairs instead of .kv partly because it meant I could just use ».gist later on in the code to effortlessly get a nice key1 => value1 display for each element. We'll modify that below but this is a good idiomatic start.
The .head and .tail calls are the idiomatic way to create small lists of the first and last N elements from an invocant list (provided it's not lazy; more about that in a mo).
Given this initial solution, say seq-range-gist (0,1 ... Inf)[^10] displays:
0 => 0
1 => 1
2 => 2
...
8 => 8
9 => 9
Next, we want to be able to "drop just the first element ... from the printed output". Unfortunately say seq-range-gist (0,1 ... Inf)[1..9] displays:
0 => 1
1 => 2
2 => 3
...
7 => 8
8 => 9
We want the number on the left of the => to retain the numbering of the original sequence. To enable this we split the underlying sequence from the range that we want extracted. We add a second parameter/argument #range, and append [#range] to the second line of the sub:
sub seq-range-gist ( #seq, #range ) {
my #pairs = #seq.pairs[#range];
Now we can write say seq-range-gist (0,1 ... Inf), 1..9 to display:
1 => 1
2 => 2
3 => 3
...
8 => 8
9 => 9
In your question you used the format aINDEX = VALUE rather than INDEX => VALUE. To allow customization of the gist, we add a third &gist routine parameter/argument and invoke that instead of the built in .gist method:
sub seq-range-gist ( #seq, #range, :&gist ) {
my #pairs = #seq.pairs[#range];
join "\n", #pairs.head(3)».&gist, '...', #pairs.tail(2)».&gist
}
Note how the "method" invocations in the body of seq-range-gist sub are now .&gist, not .gist. The syntax .&foo invokes a sub &foo (which is typically invoked by writing just foo), passing the invocant on the left of the . as a $_ argument to the sub.
Note also that I've made the &gist parameter a named one by preceding it with a :.
So now say seq-range-gist (0,1 ... Inf), 1..9, gist => { "a{.key} = {.value}" } displays:
a1 = 1
a2 = 2
a3 = 3
...
a8 = 8
a9 = 9
Adding polish
The rest of this answer is bonus material for readers who care about polish.
say seq-range-gist (0, 1, 2, 3), ^3 displays:
0 => 0
1 => 1
2 => 2
...
1 => 1
2 => 2
Oops. And even if there were more pairs than the head and tail combined, so at least we didn't get repeated lines, it'd still be pointless using the head, ..., tail approach to elide just one or two elements. Let's change the last statement in the sub body to eliminate these issues:
join "\n",
#pairs < $head + $tail + 3 # Of course, the 3 is a bit arbitrary
?? #pairs».&gist
!! (#pairs.head($head)».&gist, '...', #pairs.tail($tail)».&gist)
Next, it would be nice if the sub did something useful if called without a range or gist. We can mostly fix that by giving the #range and &gist parameters suitable defaults:
sub seq-range-gist (
#seq,
#range = #seq.is-lazy ?? ^100 !! ^#seq,
:&gist = { .gist }
) {
If #seq is not lazy, then #range defaults to the full range of #seq. If #seq is infinite (in which case it's also lazy), then the upto 100 default is fine. But what if #seq is lazy but yields less than 100 defined values? To cover this case we append .grep: *.value.defined to the #pairs declaration:
my #pairs = #seq.pairs[#range].grep: *.value.defined;
Another simple improvement would be optional head and tail parameters, leading to a final polished solution:
sub seq-range-gist (
#seq,
#range = #seq.is-lazy ?? ^100 !! ^#seq,
:$head = 3,
:$tail = 2,
:&gist = { .gist }
) {
my #pairs = #seq.pairs[#range].grep: *.value.defined;
join "\n",
#pairs <= $head + $tail + 2
?? #pairs».&gist
!! (#pairs.head($head)».&gist, '...', #pairs.tail($tail)».&gist)
}

my #seq = my $a=0, {++$a} ... *;
my \i = 0;
say( 'a' ~ (i+$_) Z=> (i+$_) ) for #seq[^5];
print "------\n";
my \j = 1;
say( 'a'.succ ~ (j+$_) Z=> (j+$_) ) for #seq[^5];
Output:
(a0 => 0)
(a1 => 1)
(a2 => 2)
(a3 => 3)
(a4 => 4)
------
(b1 => 1)
(b2 => 2)
(b3 => 3)
(b4 => 4)
(b5 => 5)
I recognize the above doesn't include your 'bespoke' gist ellipsis conditional ( ($^k < 4 or $^k > 7) ), but you seem to have come up with a more elegant way of writing that in the comments. Still (if you don't want to use skip), number keys yourself and include an offset such as i or j indicating how many elements of #seq you wish to skip.
Addendum: Below is an attempt at implementing your 'bespoke' gist ellipsis conditional (using grep):
my #seq = my $a=0, {++$a} ... *;
my \i = 0; my \m = 4; my \n = 7;
do for #seq[^10].grep({4 > $_ or $_ > 7 }) {
say 'a' ~ (i+$_) Z=> (i+$_);
if $_ == 3 {print "...\n"};
}
Output when \i = 0:
(a0 => 0)
(a1 => 1)
(a2 => 2)
(a3 => 3)
...
(a8 => 8)
(a9 => 9)
Output when \i = 1:
(a1 => 1)
(a2 => 2)
(a3 => 3)
(a4 => 4)
...
(a9 => 9)
(a10 => 10)

Related

How to correctly decontainerize the value side of a Pair object

I'm building up several Pair objects in a loop, and I use the same scalar variable (albeit with a different value) for the value of each of them.
As a simplified example of what I'm doing, consider
my #list;
my $acc = '';
for 1..30 -> $i {
if $i % 5 == 4 {
#list.push($i => $acc);
$acc = '';
} else {
$acc = "$acc $i";
}
}
say #list;
(My actual code is, of course, more complicated and reads from a file rather than a predefined range, so I can't simply eliminate the loop altogether like we theoretically could here)
We accumulate strings containing sequences of numbers written out, creating a pair mapping some of the numbers to sequences of values below that number.
I want the output of this program to be
[4 => 1 2 3 9 => 5 6 7 8 14 => 10 11 12 13 19 => 15 16 17 18 24 => 20 21 22 23 29 => 25 26 27 28]
However, I currently get
[4 => 30 9 => 30 14 => 30 19 => 30 24 => 30 29 => 30]
which, I understand, is because Pair keeps the container when I assign a scalar to its value field, so I'm really creating six pairs, all of whose values point to the same (mutable) container.
The documentation indicates this, and it even suggests a way around it
It is worth noting that when assigning a Scalar as value of a Pair the value holds the container of the value itself. This means that it is possible to change the value from outside of the Pair itself:
...
It is possible to change the above behavior forcing the Pair to remove the scalar container and to hold the effective value itself via the method freeze
which works. If I replace #list.push($i => $acc) with
my $pair = ($i => $acc);
$pair.freeze;
#list.push($pair);
then the code produces the expected output. Problem is, freeze is deprecated, and the only code listed under the deprecation warning as a possible replacement is
$p.=Map.=head.say; # OUTPUT: «orange␤»
which looks like it's converting the Pair to a Map and then back to do a sort of shallow-copy. Unfortunately, this doesn't seem to work, as #list.push(($i => $acc).Map.head); produces the original (incorrect) output.
So, since Pair.freeze is evidently deprecated, what is the correct way to decontainerize the value size of a Pair object in Raku now?
You are very close.
To get an idea what is going on I put this line print "$i: "; dd #list; just before the end of your for loop.
Here's a sample:
19: Array #list = [4 => "", 9 => "", 14 => "", 19 => ""]
20: Array #list = [4 => " 20", 9 => " 20", 14 => " 20", 19 => " 20"]
21: Array #list = [4 => " 20 21", 9 => " 20 21", 14 => " 20 21", 19 => " 20 21"]
So, as you say, the issue is that the $acc container is just being reused. In your case, you need to set the Pair value to the contents of $acc, not to the container itself.
Either of these variants work in place of your push line:
#list.push($i => $acc<>);
#list.push($i => "$acc");
The decont <> operator explictly decontainerizes the contents of the $acc container.
Or, perhaps more familiar, the "" quotes produce a new Str value with a copy of the current value of $acc.

Why can't pass a manually created Pair to method without a slip?

:5hours is a Pair, hours => 5 is also a Pair:
> DateTime.now.truncated-to('day').later(:5hours)
2022-02-14T05:00:00+08:00
> :5hours.WHAT
(Pair)
> DateTime.now.truncated-to('day').later(hours => 5)
2022-02-14T05:00:00+08:00
> (hours => 5).WHAT
(Pair)
However, when I create a Pair manually, it doesn't match the signatures of later:
> DateTime.now.truncated-to('day').later(Pair.new('hours', 5))
Cannot resolve caller later(DateTime:D: Pair:D); none of these signatures match:
(Dateish:D: *%unit --> Dateish:D)
(Dateish:D: #pairs, *%_)
in block <unit> at <unknown file> line 1
But use a vertical before the Pair parameter is ok:
> DateTime.now.truncated-to('day').later(|Pair.new('hours', 5))
2022-02-14T05:00:00+08:00
So what's the difference between :5hours, Pair.new('hours', 5) and hours => 5? Why can't pass a manually created Pair such as Pair.new('hours', 5) to later method?
Aren't the following two the same thing, right?
> :5hours === Pair.new('hours', 5) === hours => 5
True
> :5hours eqv Pair.new('hours', 5) eqv hours => 5
True
> my $pair1 = Pair.new('hours', 5); dd $pair1; # Pair $pair1 = :hours(5)
> my $pair2 = :5hours; dd $pair2; # Pair $pair2 = :hours(5)
> my $pair3 = hours => 5; dd $pair3; # Pair $pair3 = :hours(5)
> my $pair4 = 'hours' => 5; dd $pair4; # Pair $pair4 = :hours(5)
Although :5hours and hours => 5 and :hours(5) and Pair.new(hours,5) and Pair.new(key => "hours", value => 5) are all different ways to create a Pair object, only the first three are syntactic sugar to indicate a named argument.
When you pass Pair.new("hours",5) as an argument, it is considered to be a Positional argument. Observe:
sub foo(*#_, *%_) {
dd #_, %_
}
foo hours => 5;
# []
# {:hours(5)}
foo Pair.new("hours",5);
# [:hours(5)]
# {}
As to why this is this way? Well, sometimes you want to pass a Pair as a positional argument. If a Pair was always considered to be a named argument, you wouldn't be able to do so.
As to why |Pair.new("hours",5) works as a named argument? The | in this context, flattens the given object (which is usually a Capture or a Hash/Map) into the arguments to the given subroutine. The Pair in this case, is seen as a degenerate case of a Map: an immutable Map with a single key / value. Observe:
foo |Pair.new("hours",5);
# []
# {:hours(5)}
Well, probably any Associative :-)
say Pair ~~ Associative; # True
.say for (:5hours).keys; # hours
.say for (:5hours).values; # 5
Finally, the | in this context is technically not a Slip, but syntactic sugar to flatten the given value into the arguments of a call.
The syntax predates the concept of a Slip (which was introduced in 2015 during the Great List Refactor). It was only very late in 2015 that | was OK'd by #Larry to also be used to indicate a Slip, as they conceptually do similar things.

Create and populate an array of Raku objects?

I chose to redesign a portion of the previous code of mine, in this case, a chessboard, in Perl 6. The first two classes went well (or at least worked, I know so little that I can't speak to their correctness), but I'm stuck with the third. Here is the code:
#!/home/hsmyers/rakudo741/bin/perl6
# board.p6 - Beginnings of a PGN toolset. And place to start learning
# Perl 6/Raku.
use v6d;
#!___________________________________________________________
constant $size = 4;
class Piece {
my Str #namesOfPieces[$size] = <
white-rook white-knight white-bishop white-queen
>;
my Str #abrevsOfPieces[$size] = <
R N B Q K B N R
>;
my Str #symbolsOfPieces[$size] = <
♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
>;
my Str #codeptsOfPieces[$size] = (
"\x2656", "\x2658", "\x2657", "\x2655",
);
has Str $.name;
has Str $.abrev;
has Str $.symbol;
has Uni $.codept;
submethod BUILD( :$i ) {
$!name = #namesOfPieces[$i];
$!abrev = #abrevsOfPieces[$i];
$!symbol = #symbolsOfPieces[$i];
$!codept = #codeptsOfPieces[$i].NFC;
}
}
class Square {
my Int #colors[$size] = <
1 0 1 0 1 0 1 0
>;
my Str #names[$size] = <
a1 b1 c1 d1 e1 f1 g1 h1
>;
has Int $.color;
has Int $.index;
has Str $.name;
has Piece $.piece;
submethod BUILD( :$i ) {
$!color = #colors[$i];
$!index = $i;
$!name = #names[$i];
$!piece = Piece.new(:i($i));
}
}
class Board is Array {
}
my $p = Piece.new(:i(0));
$p.say;
my $s = Square.new(:i(0));
$s.say;
#!___________________________________________________________
my #b := Board.new(
Square.new(:i(0)),
Square.new(:i(1)),
Square.new(:i(2))
);
say #b;
say #b.WHAT;
When run at the cli, results in:
Piece.new(name => "white-rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC)
Square.new(color => IntStr.new(1, "1"), index => 0, name => "a1", piece => Piece.new(name => "white-
rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC))
[Square.new(color => IntStr.new(1, "1"), index => 0, name => "a1", piece => Piece.new(name =>
"white-rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC)) Square.new(color =>
IntStr.new(0, "0"), index => 1, name => "b1", piece => Piece.new(name => "white-knight", abrev =>
"N", symbol => "♘", codept => Uni.new(0x2658).NFC)) Square.new(color => IntStr.new(1, "1"), index =>
2, name => "c1", piece => Piece.new(name => "white-bishop", abrev => "B", symbol => "♗", codept =>
Uni.new(0x2657).NFC))]
(Board)
The Board class (empty as it is) is all that is left from my attempts so far. Amazingly (at least to me), it provides a degree of workability. It has variously had a "new" and a "BUILD," neither provided a working solution. The current approach doesn't work, considering that the actual count will be 64 and not 4.
My current notion is that I need to build an array of 64 Squares, which in turn will create the necessary pieces. I've tried to add to self with nothing working. Suggestions?
Inheriting from Array is probably not the best design choice here; it reveals and commits to the underlying representation of the Board, which will present refactoring challenges as the code evolves. Rather, I'd suggest that a Board has an Array of Square, which is initialized with Square objects.
Assuming the board is meant to have $size squared places, then you could do something like:
class Board {
has #.squares[$size ** 2];
method TWEAK() {
#!squares = map { Square.new(i => $_ % $size) }, ^($size ** 2);
}
}
That is, take the range from 0 up to but excluding $size squared, and then map each value into a Square instance. (We modulo the index to avoid an index out of bounds in one of the other classes.)
A 2D array may be preferable:
class Board {
has #.squares[$size;$size];
method TWEAK() {
#!squares = (map -> $i { Square.new(:$i) }, ^$size) xx $size;
}
}
Here, we map again, but this time since we're just doing one dimension we drop the modulo. The use of a named $i parameter means we can use the :$i convenience, which is short for :i($i) (there's an opportunity to do that in the code you posted also). We then take that expression producing one row, and use xx to run it $size times in order to get data for every column.
Ultimately, it will probably not be quite so simple as this; perhaps Square should take two constructor arguments, both a numeric and a letter, to form its name. That's probably best done as a map of map. Further, the initialization of Piece instances probably wants to happen in Board too; while it's been a quarter of a century since I last played chess, I'm quite sure not every square has a piece on it at the start of the game.

Creating sublists of arbitrary grouping

I'm trying to group several items from a list of strings based on the first part of each string (i.e. the part before the first tab if there is a tab or the whole string if there isn't a tab).
This works:
use Test;
my #lines = "A\tFoo"
, "A\tBar"
, "B"
, "B"
, "A\tBaz"
, "B"
;
my #expected = ["A\tFoo", "A\tBar"]
, ["B", "B"]
, ["A\tBaz"]
, ["B"]
;
my #result = group-lines(#lines);
is #result, #expected, "Grouped correctly";
sub group-lines (#records) {
my #groups;
my #current-records;
my $last-type;
for #records -> $record {
my $type = $record.split("\t")[0];
once { $last-type = $type }
if $type ne $last-type {
#groups.push: [#current-records];
#current-records = ();
}
#current-records.push: $record;
LAST { #groups.push: [#current-records] }
}
return #groups;
}
But it seems so verbose. Isn't there a shorter way to do this in Perl 6? Please note that I only want to group like items that are consecutive members of the original list.
(update) The order within groups is important.
UPDATE
Here is a more numerically oriented example. It groups numbers based on divisibility of subsequent numbers by the first number.
#!/bin/env perl6
use Test;
my #numbers = 2, 4, 6, 3, 6, 9, 12, 14;
my #expected = [2, 4, 6], [3, 6, 9, 12], [14];
my #result = group-nums(#numbers);
is #result, #expected, "Grouped correctly";
sub group-nums (#numbers) {
my #groups;
my #current-group;
my $denominator = #numbers[0];
for #numbers -> $num {
if $num % $denominator {
#groups.push: [#current-group];
#current-group = ();
}
#current-group.push: $num;
}
#groups.push: [#current-group];
return #groups;
}
You can use categorize (or categorize-list, or the classify variations if you want elements to be present in more than one category). Since your groupings are dynamic, depending on the keys that have come before, use a state variable to remember what has come before. The second example is easy because while the order matters, it's not prevented from re-adding elements to an older group:
my #numbers = <2 4 6 3 6 9 12 14>;
#numbers.classify: {
state $denom = $_; if $_ !%% $denom { $denom = $_ }; $denom;
};
# result: {2 => [2 4 6], 3 => [3 6 9 12], 14 => [14]}
Your first example needs to distinguish each grouping from groupings that came before, so the quick and dirty way is to index each group, so you can have two A groups:
my %result = #lines.classify: {
state $index = 0; # first group is group 0
state $prefix = .split("\t")[0]; # The first prefix is based on the first string
if !.starts-with($prefix) {
$prefix = .split("\t")[0]; # This is a new prefix. Remember it.
++$index; # start a new group
};
($index<> => $prefix<>); # Classify this element with a decontainerized pair. See note.
};
# result: {0 A => [A Foo A Bar], 1 B => [B B], 2 A => [A Baz], 3 B => [B]}
say %result.values; # output: ([B] [B B] [A Baz] [A Foo A Bar])
Do you need these to be in order? Since these two methods use hashes to store the data, the result is unordered.
Note: I used the <> operator to explicitly decontainerize the values that go into the Pair which is used as the classification value. Since this value is a hash key, without decontainerizing, the object ID (technically the .WHICH value) is used for hashing, and you will find that given $one = 1, (a => 1).WHICH !eqv (a => $one).WHICH. Hence, you remove the containers so the pair is treated as a pair of plain values, which will have the same hash key.
Note 2: The classification keys can be lists, which will result in a nested data structure. You won't need to decontainerize the keys, and you won't need to worry about forgetting the order. The only annoyance is an extra level of nesting in the output. To get a nested result, your classification key would be ($index, $prefix).
Here is a bit of functionally-inspired solution, though maybe a bit convoluted:
use Test;
my #lines = "A\tFoo"
, "A\tBar"
, "B"
, "B"
, "A\tBaz"
, "B"
;
my #expected = ["A\tFoo", "A\tBar"]
, ["B", "B"]
, ["A\tBaz"]
, ["B"]
;
my #eq = #lines.map(*.split("\t")[0]).rotor(2 => -1).map({ [eq] .list});
my #result = [#lines[0],],;
for #lines[1..*] Z #eq -> ($line, $eq) {
#result.push([]) unless $eq;
#result[*-1].push: $line;
}
plan 1;
is-deeply #result, #expected;
The idea is that #eq contains for each position (except the first) a True if the previous element has the same prefix as the current one.
But we don't pretend that Lisp is the One True God, and car and cdr are Her prophets, we can inline that decision, simply by using the array index to access the previous element when we need it:
my #result;
for #lines.kv -> $idx, $elem {
#result.push([]) if $idx == 0 || $elem.split("\t")[0] ne #lines[$idx-1].split("\t")[0];
#result[*-1].push: $elem;
}
plan 1;
is-deeply #result, #expected;

How to build lazy lists with defined generators and is there a "takeWhile" alternative?

I am reading through perl6intro on lazy lists and it leaves me confused about certain things.
Take this example:
sub foo($x) {
$x**2
}
my $alist = (1,2, &foo ... ^ * > 100);
will give me (1 2 4 16 256), it will square the same number until it exceeds 100. I want this to give me (1 4 9 16 25 .. ), so instead of squaring the same number, to advance a number x by 1 (or another given "step"), foo x, and so on.
Is it possible to achieve this in this specific case?
Another question I have on lazy lists is the following:
In Haskell, there is a takeWhile function, does something similar exist in Perl6?
I want this to give me (1 4 9 16 25 .. )
The easiest way to get that sequence, would be:
my #a = (1..*).map(* ** 2); # using a Whatever-expression
my #a = (1..*).map(&foo); # using your `foo` function
...or if you prefer to write it in a way that resembles a Haskell/Python list comprehension:
my #a = ($_ ** 2 for 1..*); # using an in-line expression
my #a = (foo $_ for 1..*); # using your `foo` function
While it is possible to go out of one's way to express this sequence via the ... operator (as Brad Gilbert's answer and raiph's answer demonstrate), it doesn't really make sense, as the purpose of that operator is to generate sequences where each element is derived from the previous element(s) using a consistent rule.
Use the best tool for each job:
If a sequence is easiest to express iteratively (e.g. Fibonacci sequence):
Use the ... operator.
If a sequence is easiest to express as a closed formula (e.g. sequence of squares):
Use map or for.
Here is how you could write a Perl 6 equivalent of Haskell's takewhile.
sub take-while ( &condition, Iterable \sequence ){
my \iterator = sequence.iterator;
my \generator = gather loop {
my \value = iterator.pull-one;
last if value =:= IterationEnd or !condition(value);
take value;
}
# should propagate the laziness of the sequence
sequence.is-lazy
?? generator.lazy
!! generator
}
I should probably also show an implementation of dropwhile.
sub drop-while ( &condition, Iterable \sequence ){
my \iterator = sequence.iterator;
GATHER: my \generator = gather {
# drop initial values
loop {
my \value = iterator.pull-one;
# if the iterator is out of values, stop everything
last GATHER if value =:= IterationEnd;
unless condition(value) {
# need to take this so it doesn't get lost
take value;
# continue onto next loop
last;
}
}
# take everything else
loop {
my \value = iterator.pull-one;
last if value =:= IterationEnd;
take value
}
}
sequence.is-lazy
?? generator.lazy
!! generator
}
These are only just-get-it-working examples.
It could be argued that these are worth adding as methods to lists/iterables.
You could (but probably shouldn't) implement these with the sequence generator syntax.
sub take-while ( &condition, Iterable \sequence ){
my \iterator = sequence.iterator;
my \generator = { iterator.pull-one } …^ { !condition $_ }
sequence.is-lazy ?? generator.lazy !! generator
}
sub drop-while ( &condition, Iterable \sequence ){
my \end-condition = sequence.is-lazy ?? * !! { False };
my \iterator = sequence.iterator;
my $first;
loop {
$first := iterator.pull-one;
last if $first =:= IterationEnd;
last unless condition($first);
}
# I could have shoved the loop above into a do block
# and placed it where 「$first」 is below
$first, { iterator.pull-one } … end-condition
}
If they were added to Perl 6/Rakudo, they would likely be implemented with Iterator classes.
( I might just go and add them. )
A direct implementation of what you are asking for is something like:
do {
my $x = 0;
{ (++$x)² } …^ * > 100
}
Which can be done with state variables:
{ ( ++(state $x = 0) )² } …^ * > 100
And a state variable that isn't used outside of declaring it doesn't need a name.
( A scalar variable starts out as an undefined Any, which becomes 0 in a numeric context )
{ (++( $ ))² } …^ * > 100
{ (++$)² } …^ * > 100
If you need to initialize the anonymous state variable, you can use the defined-or operator // combined with the equal meta-operator =.
{ (++( $ //= 5))² } …^ * > 100
In some simple cases you don't have to tell the sequence generator how to calculate the next values.
In such cases the ending condition can also be simplified.
say 1,2,4 ...^ 100
# (1 2 4 8 16 32 64)
The only other time you can safely simplify the ending condition is if you know that it will stop on the value.
say 1, { $_ * 2 } ... 64;
# (1 2 4 8 16 32 64)
say 1, { $_ * 2 } ... 3;
# (1 2 4 8 16 32 64 128 256 512 ...)
I want this to give me (1 4 9 16 25 .. )
my #alist = {(++$)²} ... Inf;
say #alist[^10]; # (1 4 9 16 25 36 49 64 81 100)
The {…} is an arbitrary block of code. It is invoked for each value of a sequence when used as the LHS of the ... sequence operator.
The (…)² evaluates to the square of the expression inside the parens. (I could have written (…) ** 2 to mean the same thing.)
The ++$ returns 1, 2, 3, 4, 5, 6 … by combining a pre-increment ++ (add one) with a $ variable.
In Haskell, there is a takeWhile function, does something similar exist in Perl6?
Replace the Inf from the above sequence with the desired end condition:
my #alist = {(++$)²} ... * > 70; # stop at step that goes past 70
say #alist; # [1 4 9 16 25 36 49 64 81]
my #alist = {(++$)²} ...^ * > 70; # stop at step before step past 70
say #alist; # [1 4 9 16 25 36 49 64]
Note how the ... and ...^ variants of the sequence operator provide the two variations on the stop condition. I note in your original question you have ... ^ * > 70, not ...^ * > 70. Because the ^ in the latter is detached from the ... it has a different meaning. See Brad's comment.