On Mar 21, 2012, at 11:49 PM, Jonathan Lang wrote:

What I want to know is whether there's a way to define a step function that's based in part or in whole on the current term's index. For example, how would I use infix:<...> to generate the perfect squares between 0 and 100? Namely, '0,1,4,9,16,25,36,49,64,81,100'. For example, is Perl 6 set up to try to pass the current element's index into the step function by means of a named parameter?

I would hope for something like:

  -> :index { $index * $index } ... 100

or

  1, *+:index ... * # 1,3,6,10,15,21,28,36,45,...

(Note: I _don't_ expect the above to work as is; they're merely intended to convey a rough idea of what I _do_ want.)

If not, how _would_ I generate these lists?


In real life, you would just use this:
   my @squares = map { $_ * $_ }, 0..10;
or, for an infinite list, use a binding instead of an assignment:
   my @squares := map { $_ * $_ }, 0..*;
But you were asking about ... specifically :^)

I have run into the same need for something like :index, while
playing with RosettaCode tasks like "Continued_fraction".
Your "squares" example will be clearer than the RC tasks.

At first, I tried to trick the compiler, by *binding* the list to an
array, then referring to that bound array from within the closure.
   my @squares := 0, { @squares.elems ** 2 } ... *;
This worked, but only as an artifact of the then-current Rakudo
implementation. It is not specced to work, and I was told in
freenode/#perl6 not to rely on that behavior.

On freenode/#perl6, I was pointed to part of the spec that I had overlooked.
The "sequence operator" is defined in S03:
   http://perlcabal.org/syn/S03.html#List_infix_precedence
Buried in its definition is this gem:
   http://perlcabal.org/syn/S03.html#line_1884
       The function may also be slurpy (n-ary), in which case all
       the preceding values are passed in (which means they must
       all be cached by the operator, so performance may suffer,
       and you may find yourself with a "space leak").

That means that this will work:
   sub sq_gen (*@a) { @a.elems ** 2 };
   my @squares = 0, &sq_gen ... {$_ >= 100};
   say ~@squares;
(Note that the asterisk in "*@a" is mandatory)
or, infinitely:
   sub sq_gen (*@a) { @a.elems ** 2 };
   my @squares := 0, &sq_gen ... *;
   say ~@squares[^11];

Great! However, I wanted to do it inline, without a separately-defined sub. If it wasn't for that required asterisk, then I could use a placeholder variable to do this:
   my @squares = 0, { @^a.elems ** 2 } ... {$_ >= 100};
FAIL! (Niecza)
Unhandled exception: Nominal type check failed in binding '@a' in 'ANON'; got Int, needed Positional

The last piece of the puzzle is the "arrow sub" syntax, more commonly
seen in "for" loops:
   my @squares = 0, (-> *@a { @a.elems ** 2 }) ... {$_ >= 100};
Or, more concisely:
   my @squares = 0, -> *@a { +@a ** 2 } ... * >= 100;

This works!

Well, it works in Niecza. It does not (yet) work in Rakudo:
15:25 <Util> perl6: my @squares := 0, (-> *@a { @a.elems ** 2 }) ... *; say ~@squares[^11]; 15:25 <p6eval> ..niecza v15-4-g1f35f89: OUTPUT<<0 1 4 9 16 25 36 49 64 81 100NL>>
        15:25 <p6eval> ..rakudo 1a468d: OUTPUT<<0 0 0 0 0 0 0 0 0 0 0NL>>

For your second example (1,3,6,10,15,21,28,36,45,...), it would look like this:
        my @triangle = 1, (-> *@a { @a[*-1] + @a.elems + 1 }) ... {$_ >= 45};
Note that this particular sequence is just the "triangle numbers", which
has a shorter form via triangular reduce:
   my @triangle = [\+] 1..9;

After writing all the above, it occurred to me that the use of @_ should
implicitly define a closure as slurpy/n-ary. That would remove the need
for the arrow, and make the code much less ugly.
Also, the first value (0) might be unnecessary. The spec says that it
should not be required when the closure is 0-ary, but I think that
should also be true for slurpy/n-ary closures.
These work in Niecza:

        my @squares := { @_ ** 2 } ... *;

        my @triangle := 1, { @_[*-1] + @_ + 1 } ... *;

--
Hope this helps,
Bruce Gray
(Util of PerlMonks)

Reply via email to