How to correctly decontainerize the value side of a Pair object - raku

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.

Related

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.

Printing mathematical series concisely in Raku

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)

Perl6 Using Proxy to trigger on attribute access

I am trying to implement a trigger on write access to a perl6 Class attribute. I cannot figure out the cause of the error...
... I got this notion from How does one write custom accessor methods in Perl6?
1 #!/usr/bin/env perl6
2
3 class MeasureSP {
4
5 has Real $!value;
6 has Str $.units;
7
8 submethod BUILD( :$!value, :$!units ) {}
9
10 method value( Real $newval? ) is rw {
11 return Proxy.new:
12 FETCH => sub ($) { $!value },
13 STORE => sub ($, $newval) { $!value = $newval },
14 }
15
16 }
17 my MeasureSP $m-sp = MeasureSP.new( value => 23, units => 'metres' );
18 say $m-sp.units; #metres
19 say $m-sp.value; #23
20 $m-sp.value = 1;
21 # Cannot assign to a readonly variable or a value
22 #in block <unit> at ./retry.p6 line 20
This behaviour seems to have changed - this code was working fine back in June 18 - but I want to be sure I am in step with the latest thinking.
Any help would be very much appreciated !
Either remove the return:
method value( Real $newval? ) is rw {
Proxy.new:
FETCH => sub ($) { $!value },
STORE => sub ($, $newval) { $!value = $newval },
}
Or use return-rw if you really want to be explicit:
method value( Real $newval? ) is rw {
return-rw Proxy.new:
FETCH => sub ($) { $!value },
STORE => sub ($, $newval) { $!value = $newval },
}
The problem is that return will strip away any item container, so just putting is rw on the method is not enough on its own.
As to why the posted code used to work, but no longer does: Rakudo releases in the last couple of months have included a fix for a bug that meant Proxy was treated differently than Scalar in routine return handling. The Proxy was not stripped in places it should have been; now it reliably is.

perl 6 passing methods as arguments [duplicate]

I have a script like the below. Intent is to have different filter methods to filter a list.
Here is the code.
2
3 class list_filter {
4 has #.my_list = (1..20);
5
6 method filter($l) { return True; }
7
8 # filter method
9 method filter_lt_10($l) {
10 if ($l > 10) { return False; }
11 return True;
12 }
13
14 # filter method
15 method filter_gt_10($l) {
16 if ($l < 10) { return False; }
17 return True;
18 }
19
20 # expecting a list of (1..10) to be the output here
21 method get_filtered_list_lt_10() {
22 return self.get_filtered_list(&{self.filter_lt_10});
23 }
24
25 # private
26 method get_filtered_list(&filter_method) {
27 my #newlist = ();
28 for #.my_list -> $l {
29 if (&filter_method($l)) { push(#newlist, $l); }
30 }
31 return #newlist;
32 }
33 }
34
35 my $listobj = list_filter.new();
36
37 my #outlist = $listobj.get_filtered_list_lt_10();
38 say #outlist;
Expecting [1..10] to be the output here. But getting following error.
Too few positionals passed; expected 2 arguments but got 1
in method filter_lt_10 at ./b.pl6 line 9
in method get_filtered_list_lt_10 at ./b.pl6 line 22
in block <unit> at ./b.pl6 line 37
What am I doing wrong here?
Passing a method as a parameter in Perl 6 either requires you to use MOP (Meta-Object Protocol) methods, or pass the method by name (which would then do the lookup for you at runtime).
But why use methods if you're not really doing something with the object in those methods? They might as well be subs then, which you can pass as a parameter.
Perhaps this is best by example:
class list_filter {
has #.my_list = 1..20; # don't need parentheses
sub filter($ --> True) { } # don't need code, signature is enough
# filter sub
sub filter_lt_10($l) { not $l > 10 }
# filter sub
sub filter_gt_10($l) { not $l < 10 }
# private
method !get_filtered_list(&filter_sub) {
#.my_list.grep(&filter_sub);
}
# expecting a list of (1..10) to be the output here
method get_filtered_list_lt_10() {
self!get_filtered_list(&filter_lt_10);
}
}
my $listobj = list_filter.new();
my #outlist = $listobj.get_filtered_list_lt_10();
say #outlist; # [1 2 3 4 5 6 7 8 9 10]
The first sub filter, which only returns a constant value (in this case True), can be represented much more easily in the signature with an empty body.
The filter_lt_10 and filter_gt_10 subs only need the condition negated, hence the use of the not.
The get_filtered_list method is supposed to be private, so make it a private method by prefixing !.
In the get_filtered_list_lt_10 you now need to call get_filtered_list with a ! instead of a .. And you pass the filter_lt_10 sub as a parameter by prefixing the & (otherwise it would be considered a call to the sub without any parameters, which would fail).
Change the get_filtered_listto use the built-in grep method: this takes a Callable block that takes a single parameter and which should return something True to include the value of the list it works upon. Since a sub taking a single parameter is a Callable, we can just specify the sub there directly.
Hope this made sense. I tried to stay as close as possible to the intended semantics.
Some general programming remarks: it feels to me that the naming of the subs is confusing: it feels to me that they should be called filter_le_10 and filter_ge_10, because that's really what they do it appears to me. Also, if you really don't want any ad-hoc filtering, but only filtering from a specific set of predefined filters, you would probably be better of by creating a dispatch table using constants or enums, and use that to indicate which filter you want, rather than encoding this information in the name of yet another method to make and maintain.
Hope this helps.
TL;DR You told P6 what arguments to expect when calling your filter method. Then you failed to pass the agreed argument(s) when you called it. So P6 complained on your behalf. To resolve the issue, either pass the argument(s) you told P6 to expect or stop telling P6 to expect them. :)
The message says expected 2, got 1, rather than expected 1 got 0.
This is because self is implicitly passed and added to the "expected" and "got" totals in this appended bit of message detail, bumping both up by one. (This detail is perhaps Less Than Awesome, i.e. something we should perhaps consider fixing.)
When I run your code on tio I get:
Too few positionals passed; expected 2 arguments but got 1
in method filter at .code.tio line 27
in method print_filtered_list at .code.tio line 12
in block <unit> at .code.tio line 42
The method declaration method filter($l) {...} at line 27 tells P6 to expect two arguments for each .filter method call:
The invocant. (This will be bound to self.) Let's call that argument A.
A positional argument. (This will be bound to the $l parameter). Let's call that argument B.
But in &{self.filter} in line 12, while you provide the .filter method call with an argument A, i.e. an invocant argument, you don't provide an argument B, i.e. a positional argument (after filter, e.g. &{self.filter(42)}).
Hence Too few positionals passed; expected 2 arguments but got 1.
The &{self.method} syntax was new to me, so thanks for that. Unfortunately it doesn't work if parameters are needed. You can use sub as other posters mentioned, but if you need to use methods, you can get a method by calling self.^lookup, which is the use of the meta-object protocol that Elizabeth mentioned. ('^' means you're not calling a method that's part of that class, but rather part of the "shadow" class which contains the main class's guts / implementation details.)
To get a method, use run obj.^lookup(method name), and call it by passing in the object itself (often "self") as the first parameter, then the other parameters. To bind the object to the function so it doesn't need to be explicitly added each time, use the assuming function.
class MyClass {
method log(Str $message) { say now ~ " $message"; }
method get-logger() { return self.^lookup('log').assuming(self); }
}
my &log = MyClass.get-logger();
log('hello'); # output: Instant:1515047449.201730 hello
Found it. this is what worked for me.
3 class list_filter {
4 has #.my_list = (1..20);
5
6 # will be overriding this in derived classes
7 method filter1($l) { return True; }
8 method filter2($l) { return True; }
9
10 # same print method I will be calling from all derived class objects
11 method print_filtered_list($type) {
12 my #outlist = self.get_filtered_list($type);
13 say #outlist;
14 }
15
16 # private
17 method get_filtered_list($type) {
18 my #newlist = ();
19 for #.my_list -> $l {
20 my $f = "filter$type";
21 if (self."$f"($l)) { push(#newlist, $l); }
22 }
23 return #newlist;
24 }
25 }
26
27 class list_filter_lt_10 is list_filter {
28 method filter1($l) {
29 if ($l > 10) { return False; }
30 return True;
31 }
32 method filter2($l) {
33 if ($l > 10) { return False; }
34 if ($l < 5) { return False; }
35 return True;
36 }
37 }
38
39 class list_filter_gt_10 is list_filter {
40 method filter1($l) {
41 if ($l < 10) { return False; }
42 return True;
43 }
44 method filter2($l) {
45 if ($l < 10) { return False; }
46 if ($l > 15) { return False; }
47 return True;
48 }
49 }
50
51 my $listobj1 = list_filter_lt_10.new();
52 $listobj1.print_filtered_list(1);
53 $listobj1.print_filtered_list(2);
54
55 my $listobj2 = list_filter_gt_10.new();
56 $listobj2.print_filtered_list(1);
57 $listobj2.print_filtered_list(2);
58
Output:
./b.pl6
[1 2 3 4 5 6 7 8 9 10]
[5 6 7 8 9 10]
[10 11 12 13 14 15 16 17 18 19 20]
[10 11 12 13 14 15]
piojo's answer looks like it would work (though I haven't tried it).
Another approach to turning a method into a variable is to use indirection:
class Foo {
method bar($a) {
$a * 2
}
}
sub twice(&f, $x) {
f f $x
}
my $foo = Foo.new();
say twice {$foo.bar: $^a}, 1

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.