Use the Y
operator
From a quick survey, it looks like I use Y
(or one of its variants, YP
and YO
) in one out of every three Pip answers--more so as the answers get more complex.
Storing a value
Y
stands for "yank," which will be familiar to Vim users as a command that copies the current line or selection into a buffer. The unary Y
operator in Pip does something similar: it saves a copy of its operand in the y
global variable and returns the operand unchanged. Essentially, Y<expr>
is equivalent to y:<expr>
, but shorter.
This alone makes Y
(and the y
variable) useful in many cases. Need to store something in a variable and don't care which one you use? Y
saves a byte from the assignment. Need to use an expression twice? Yank it and use y
twice instead. If the expression is longer than two bytes, you'll save.
But Y
is also useful in another way:
Manipulating operator precedence
Suppose we want to count the number of 0s in the input and then tack the count onto the end of that input. (For example, input of 1001101
should result in 1001101 3
.) Counting the 0s is 0Na
, and so we would like to do a.s.0Na
. But that won't work because N
is lower precedence than .
, and the expression would parse as ((a.s).0)Na
. To enforce precedence, we can use parentheses: a.s.(0Na)
. This always works, and sometimes it's the only option.
But often, we can use Y
instead. The trick is that Y
has very low precedence--the lowest, in fact, together with P
and O
. So any expression to the right of Y
will parse as Y
's operand, while the whole Y
expression will in turn be the right operand of whatever comes to its left. And Y
will pass its operand through unchanged (plus assign it to y
, but we don't care about that as long as we're not using y
for something else). If we write a.s.Y0Na
, it parses as (a.s).(Y(0Na))
, just as if we had parenthesized 0Na
. But it only costs one byte, while the parentheses cost two.
Caveats
A Y
expression can only be used on the right side of a binary operator, not the left, because Y
will take everything to the right of it as its operand. For instance, if we wanted to prepend the count of 0s instead of appending it, we couldn't do Y0Na.s.a
--that would parse as Y(0N(a.s.a))
. Instead, we'd have to fall back on parentheses or another strategy.
There is only one y
variable, so you can't yank two different values in the same program (unless you can structure your code so that you don't need both of them at the same time). You'll have to pick one to assign to a different variable. Try it both ways and see which one saves you more bytes.
Binary operators in Pip always evaluate their left-hand side first, which means that you generally can't use the new value of y
in the same expression where you yanked it: If you want to calculate the square of a+1
, you can't do y*Ya+1
(y
won't be a+1
when it's evaluated, because a+1
hasn't been yanked yet); and you can't do Ya+1*a
(which would parse as Y(a+(1*a))
). In situations like this, you'll probably want to yank the value first, in a separate expression: Ya+1y*y
. If you absolutely need to do it in one expression, you can parenthesize the Y
part: (Ya+1)*y
. This works because the left-hand side of *
is evaluated first, so y
has the correct value when the right-hand side is evaluated.