Some external Raku golf tips
The sequence operator (...
)
This is one of the more powerful tools in Raku, especially for the numerous sequence holes. The general formation is:
$initialisation,&generator...&endpoint
0,1,*+*...*>32 # Fibonacci
These can be mix and matched. If you leave out the initialisation, if the code uses $_
, it will take the value of that variable for the first iteration from the outer scope (defaulting to (Any)
if you're in the base program). If you leave out the generator, it will try to interpolate the sequence from the initial list, otherwise just incrementing the last value. Using *
as the termination will make it loop forever (if you want until the value is falsey, you'll have to do ?*
).
{$_+1}...10 # (Any) turns into a 0, so this is the range 1 to 10. Note that you can't use *+1
0,2...10 # Even numbers from 0 to 10
1...* # Infinite range
If a range is assigned to a variable or returned, it will probably stay lazy. If you have it as a base statement, it will be evaluated.
Print inside the sequence
Rather than generate a sequence and then print the values, you can print the values inside either the generator or the termination, as both are executed for each element.
(0,1,*+*...*>32)>>.say;
0,1,{say $^a;$a+$^b}...*>32;
0,1,*+*...{.say;$_>32};
0,1,*+*...*.say+*>35;
Lambda/Blocks
The generator is a function, which can either be block {}
or a Whatever lambda. Both of these can take multiple previous elements (as shown above in the Fibonacci example), but the block can also take a list of all the previous elements as @_
(this is mutually exclusive with $_
), or no input at all. Also note that $
variables will persist in each block as usual, which can let you keep track of the length of the list, or some persistant value.
{@_.sum+@_}...* # 2**n-1
{2**$++-1}...*
{2**@_-1}...*
0,2* *+1...*
Termination
Termination is interesting. Mechanically, it functions like the smartmatch operator (~~
), which calls the ACCEPTS
method, which has some interesting interactions. This means you can use:
- String/Number. Obviously, this just terminates when that number or string appears, however it also can convert the element to that type to compare. For example, to terminate when an array reaches a certain size.
- Positional. True if the element exists in the positional (this seems a bit buggy when used on its own).
- Callable. As detailed above, the callable just needs to return something truthy. If the lambda takes more than one input (e.g.
*+*+*
), it will only start executing once the the list reaches that length.
- Regex. This will terminate if the regex matches the element. Useful for bigger numbers or strings when you can safely match only part of the element.
- Pair. For a Hash, this just looks up the value in that hash. Against a pair, it checks the value of the pairs are equal. For everything else, the weirdest (but most useful) interaction is that "the invocant Pair's key is treated as a method name". So practically,
1...:is-prime
will be the same as 1...&is-prime
, which can be combined well with the next item.
- Junction. These are combinations of the above types that will all be called (including with shortcircuiting!). There are three types of useful junctions here,
&|^
, all similar to their bitwise formulations. If you need to terminate for two conditions, you can have a termination &cond1& &cond2
(or, if you do it right, &cond1&:cond2
). If you remember that say
and put
always return True, this can be combined in some very unique ways, such as
1...&say&100; # Print 1 to 100
1...{$_%2}&:say&99; # Print odds from 1 to 99
1...*%2&:say&99; # Doesn't work, * swallows surroundings
1...&(*%2)&:say&99; # Instead do this
Example Templates
Most sequence holes involve either filtering from 0 to n, or some transformation of previous elements, so a good start is to use one of these templates:
0...&cond&:say&n
1,&gen...&say&n
1,{.say;code}...n
1,&gen...*.say+*>n
1,&gen...&say^ ^n
Remember to be creative with termination conditions, as well as where you put the printing section of the code.