11
\$\begingroup\$

What general tips do you have for golfing in Retina? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Retina (e.g. "remove comments" is not an answer). Please post one tip per answer.

For reference, the online compiler is here.

@Sp3000 pointed out there is also Tips for Regex Golf. Answers here should focus specifically on Retina features and not on general regex golfing tips.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Related: Tips for regex golf \$\endgroup\$
    – Sp3000
    Feb 9, 2016 at 21:29
  • \$\begingroup\$ Hmmm, I've been holding off posting this because Retina is still much in development and I was afraid most answers would end up being plain regex golfing tips, not very specific to Retina. But we might as well give it a go, I guess... :) \$\endgroup\$ Feb 9, 2016 at 21:32
  • \$\begingroup\$ @MartinBüttner You and some others have given me a lot of good tips and hints since I started looking at Retina, so I think its probably about time for this. I added a clarification that general regex tips should go to the linked question. \$\endgroup\$ Feb 9, 2016 at 21:35
  • 1
    \$\begingroup\$ @MartinBüttner Here is as good a place as any to ask - I've been wondering for a while - out of curiosity what is the inspiration for the name "Retina"? I assume the "Re" part is for Regular Expression, but what about the "tina"? \$\endgroup\$ Feb 9, 2016 at 21:38
  • 3
    \$\begingroup\$ @DigitalTrauma I was trying to come up with a nice word that would work as an acronym, but failed. The word "retina" was quite close to some of the attempts, and I liked the word. I never managed to retcon it into an acronym though and have since given up on that. So yeah the "re" is sort of for "regular expressions" and maybe the "n" for ".NET", but ultimately it's just a word that sounded nice. \$\endgroup\$ Feb 9, 2016 at 21:41

4 Answers 4

3
\$\begingroup\$

Combine loops if possible

In non-trivial computations you'll often find yourself using several loops to process data:

+`stage1
+`stage2
+`stage3

So this runs stage1 until the output converges, then stage2 until the output converges and then stage3 until the output converges.

However, it's always worth examining the stages in detail. Sometimes it's possible to run the loop in an interleaved fashion as stage1, stage2, stage3, stage1, stage2, stage3, ... instead (this depends a lot on what the stages actually do, but sometimes they make completely orthogonal changes or work well as a pipeline). In this case you can save bytes by wrapping them in a single loop:

{`stage1
stage2
}`stage3

If stage1 is the first stage or stage3 is the last stage of the program you can then even omit on of those parentheses as well (which means this can already save bytes for a loop of two stages).

A recent use of this technique can be seen in this answer.

\$\endgroup\$
2
\$\begingroup\$

Splitting strings into chunks of equal length n

As in most "normal" languages TMTOWTDI (there's more than one way to do it). I'm assuming here that the input doesn't contain linefeeds, and that "splitting" means splitting it into lines. But there are two quite different goals: if the length of the string isn't a multiple of the chunk length, do you want to keep the incomplete trailing chunk or do you want to discard it?

Keeping an incomplete trailing chunk

In general, there are three ways to go about the splitting in Retina. I'm presenting all three approaches here, because they might make a bigger difference when you try to adapt them to a related problem. You can use a replacement and append a linefeed to each match:

.{n}
$&¶

That's 8 bytes (or a bit less if n = 2 or n = 3 because then you can use .. or ... respectively). This has one issue though: it appends an additional linefeed if the string length is a multiple of the chunk length.

You can also use a split stage, and make use of the fact that captures are retained in the split:

S_`(.{n})

The _ option removes the empty lines that would otherwise result from covering the entire string with matches. This is 9 bytes, but it doesn't add a trailing linefeed. For n = 3 it's 8 bytes and for n = 2 it's 7 bytes. Note that you can save one byte overall if the empty lines don't matter (e.g. because you'll only be processing non-empty lines and getting rid of linefeeds later anyway): then you can remove the _.

The third option is to use a match. With the ! option we can print all the matches. However, to include the trailing chunk, we need to allow for a variable match length:

M!`.{1,n}

This is also 9 bytes, and also won't include a trailing linefeed. This also becomes 8 bytes for n = 3 by doing ..?.?. However note that it reduces to 6 bytes for n = 2 because now we only need ..?. Also note that the M can be dropped if this is the last stage in your program, saving one byte in any case.

Discarding an incomplete trailing chunk

This gets really long if you try to do it with a replacement, because you need to replace the trailing chunk with nothing (if it exists) and also with a split. So we can safely ignore those. Interestingly, for the match approach it's the opposite: it gets shorter:

M!`.{n}

That's 7 bytes, or less for n = 2, n = 3. Again, note that you can omit the M if this is the last stage in the code.

If you do want a trailing linefeed here, you can get that by append |$ to the regex.

Bonus: overlapping chunks

Remember that M has the & option which returns overlapping matches (which is normally not possible with regex). This allows you to get all overlapping chunks (substrings) of a string of a given length:

M!&`.{n}
\$\endgroup\$
3
  • \$\begingroup\$ Is it somehow possible to split a string exactly in halve with variable length? So 123456 becomes 123\n456 and 1234567890 becomes 12345\n67890? \$\endgroup\$ Oct 25, 2018 at 18:47
  • 1
    \$\begingroup\$ @KevinCruijssen I don't think I added any specific feature for that. You'll probably have to use balancing groups: tio.run/##K0otycxLNPyvquGe8D/YIEHD3sZWQ09TW1PD3hbI1jW0A3JUNP//… If you don't mind a trailing linefeed, you can omit the ?=. \$\endgroup\$ Oct 26, 2018 at 7:22
  • \$\begingroup\$ I was able to complete the challenge where I thought I needed it differently, but the balancing groups is indeed very useful! I knew it had to be something along those lines, but my regex/Retina skills are not nearly good enough. Thanks for answering! :) \$\endgroup\$ Oct 26, 2018 at 7:26
1
\$\begingroup\$

Arithmetic in Retina 1

In Retina you need to do your arithmetic in unary. However, Retina 1 makes things slightly easier for you in certain cases, principally because of two constructs:

  • The repetition operator * accepts an expression as its RHS, whereas the $* operator in Retina 0.8.2 only accepted a character as its RHS
  • Retina 1 now includes a $(...) grouping construct, which accepts the length modifier $.(...)

It thus becomes possible to perform some arithmetic operations in both unary or decimal, depending on which is convenient. In particular, the group length construct $.(...) calculates the sum of the lengths of its contents, taking any products using arbitrary precision integers, so it's not limited in value.

Addition

This is straightforward.

  • Convert any decimal numbers to unary using *_
  • Concatenate the numbers to give a unary result
  • Group the sum using $(...) if you intend to multiply it or $.(...) to convert it to decimal

2-argument examples:

  • Unary + Unary = Unary: $1$2
  • Unary + Unary = Decimal: $.($1$2)
  • Unary + Decimal = Unary: $1$2*_
  • Unary + Decimal = Decimal: $.($1$2*) (see below)
  • Decimal + Decimal = Unary: $1*_$2*_
  • Decimal + Decimal = Decimal: $.($1*_$2*)

Multiplication

Note that * normally takes one decimal and one unary parameter and returns a unary result.

  • If the numbers are all decimal, add an extra unary parameter of 1
  • Convert all but one number to decimal using the length modifier
  • Take the product to give a unary result
  • Convert the product to decimal by enclosing in $.(...) if desired

2-argument examples:

  • Unary * Unary = Unary: $.1*$2
  • Unary * Unary = Decimal: $.($.1*$2)
  • Decimal * Unary = Unary: $1*$2
  • Decimal * Unary = Decimal: $.($1*$2)
  • Decimal * Decimal = Unary: $1*$2*_
  • Decimal * Decimal = Decimal: $.($1*$2*) (see below)

A Retina replacement string to take the cube of a matched decimal number would be $.(*** (see below).

Further savings

  • Only the first non-negative integer contained in the the LHS of a * operator is significant; there is no limit on junk characters (including -s). In particular this makes it easier to use $&.
  • If the LHS of a * is indeed $&, then it is not necessary at the start of a $( construct or replacement string or after another *. Example: $.(*__)
  • The _ after a * is not necessary at the end of a $( construct or replacement string. Example: $.($1*_$2*)
  • It is not necessary to close your $( constructs if they end at the end of the replacement string. Example: $.($1$2
  • When adding the constant values 1 or 2 it is shorter to express them in unary.
\$\endgroup\$
2
  • \$\begingroup\$ (remark: there are some answers to divide two numbers in retina under the corresponding challenge. codegolf.stackexchange.com/questions/114719/…) \$\endgroup\$
    – DELETE_ME
    Mar 7, 2021 at 1:56
  • \$\begingroup\$ @user202729 This tip is about the new opportunities to perform decimal-based arithmetic possible in Retina 1, which don't apply to division. \$\endgroup\$
    – Neil
    Mar 7, 2021 at 12:06
1
\$\begingroup\$

Using balancing groups to capture the same count twice.

Occasionally you need to capture a balancing group twice, such as to match the substring nice when the letters are equally spaced in the input. The naive approach is to capture the characters between n and i twice, and then use balancing groups to ensure that the same number of character exist between i and c and c and e, like this:

n((.))*i(?<-1>.)*(?(1)^)c(?<-2>.)*(?(2)^)e

However, you can capture into capture group 2 while popping from capture group 1, allowing you to then pop the same count from capture group 2 later, saving a byte:

n(.)*i(?<2-1>.)*(?(1)^)c(?<-2>.)*(?(2)^)e

(Normally you would use (?!) to force the match to fail if the count is too low but obviously in code golf we just use the shortest regex that we know will fail; ^ usually works.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.