12
\$\begingroup\$

Pip is an imperative golfing language with infix operators. It also borrows some features from functional and array programming.

What general tips do you have for golfing in Pip? I'm looking for approaches and tricks that are commonly useful for code golf and are specific to Pip (e.g. "remove comments" is not an answer).

Please post one tip per answer.

\$\endgroup\$
  • 4
    \$\begingroup\$ "Pip" "Tip" they rhyme perfectly! \$\endgroup\$ – user96495 Aug 31 at 2:42
  • 2
    \$\begingroup\$ Nice. I am ready to abuse all the answers from this thread. \$\endgroup\$ – Razetime Aug 31 at 4:05
  • 2
    \$\begingroup\$ I thought it was PIP (Python Package Installer) \$\endgroup\$ – pavi2410 Sep 1 at 7:09
  • \$\begingroup\$ @pavi2410 There's 2 of them \$\endgroup\$ – user Sep 1 at 13:02
7
\$\begingroup\$

Use preinitialized variables

Pip has many global variables that are pre-initialized so you can avoid manually writing a number/string/something else out yourself.

Here are some of them (complete list):

  • _ Identity function (== {a})

  • h 100

  • i 0

  • k ", "

  • l Empty list

  • m 1000 (mnemonic: Roman numeral M)

  • n Newline character

  • o 1

  • s Space character

  • t 10

  • v -1

  • w Matches whitespace \s+

  • x Empty string

  • z Lowercase alphabet a to z

  • B Block that returns its second argument ({b})

  • G Block that returns its argument list ({g})

  • AZ Uppercase alphabet A to Z

  • CZ Lowercase consonants b to z

  • PA All Printable ASCII characters, 32 through 126

  • PI \$\pi\$ (3.141592653589793)

  • VW Lowercase vowels a to u

  • VY Lowercase vowels a to y

  • XA Matches one Latin letter [A-Za-z] (there are more X* commands)

Link to documentation

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Should I make this community wiki? I’m not sure know what tips are supposed to look like \$\endgroup\$ – user Aug 31 at 3:10
  • 4
    \$\begingroup\$ This is your tip. You are not required to make it community wiki. Just give it a decent title, and it'll be all good. Feel free to link to the docs here for more detailed info as well. \$\endgroup\$ – Razetime Aug 31 at 4:04
  • \$\begingroup\$ @Razetime Thanks for the clarification. I'll also edit my answer in a while to make it a little better. \$\endgroup\$ – user Aug 31 at 12:56
5
\$\begingroup\$

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.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Replace simple functions with lambdas

Lambdas are a generally good way to save 2 bytes(at the very least), as compared to a regular function that requires braces.

Pip's lambdas, however, do not mimic the general attributes of a lambda in say, Python. You cannot shorten all standalone functions into lambdas.

They're an extension of the identity function _, where _ = {a}. This means that you can create a lambda out of a function only if you are writing a function with a single expression.

Lambdas in Pip have some interesting uses other than single expressions. For example, you can reference the main function's arguments without yanking/copying them.

From Dlosc's tutorial on functions:

The function _+a adds its argument to the program's first command-line argument. a still has its top-level value because it's not inside curly braces.

The catch is that you cannot use commands(If, Else if, Loops) and everything must come down to one statement, as it is generally with applying operators on functions.

Due to those restrictions, Pip lambdas are generally better suited for small mapping or filtering functions. Happy golfing!

If you do find an interesting use case, do tell me.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

M can map a value instead of a function

Normally, you would use M like this:

UC_M"abc"

with the left-hand side being a function and the right-hand side being some iterable you want to map it to. If you need to swap the arguments, "abc"MUC_ works too.

But in the case where you just want to replace every item in the iterable with a constant value, M can do that too--just give it a non-function as its left-hand side:

42M"abc"

This will return [42;42;42]: one 42 for every character in "abc". If you don't mind that you're getting a list and not a string, this is one byte shorter than 42X#"abc", and it's two bytes shorter than the list equivalent 42RL#"abc".

The value can be literally anything* that's not a function: Scalar, Pattern, List, Range, or even Nil.

This trick also works with some other map operators:

  • aMJb joins the results into a string after mapping (may be particularly useful when a is not a Scalar to begin with)
  • aMMb will give you a list of lists of a, the lengths of the sublists matching the lengths of the items in b
  • aMCb will give you a bxb grid (nested list) of a

That last one is a useful alternative to ZG when you want a value other than 0 (for example).


* Technically, it'll behave differently if given a list that contains a function.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Ah, the asterisk program. \$\endgroup\$ – Razetime Sep 4 at 9:41
1
\$\begingroup\$

Shortcuts for certain lists

Normally, if you need a list of constants, you just enclose the items in square brackets, with separators as necessary ([4 8 15 16 23 42]). But there are shorter ways to construct some commonly used lists:

  • First, don't forget you can use preset variables if you haven't reassigned them. The empty list can be l instead of []. Using i for 0 and o for 1 in a list can save a byte (or even two) if they are next to another number, since lowercase variables don't require a separator: [0 5][i5]. Using v for -1, t for 10, h for 100, or m for 1000 also saves bytes directly, since the variable names are shorter than the numbers they represent.
  • Use a range if possible: [0 1][io],2. This still saves a byte if it's 1-based ([o2]\,2) or starts at some other number ([v0]v,1).
  • For a list of digits, split a number: [1]^1; [3o4o5 9]^314159; [oi]^t. This works for characters, too: ['#]^'#; ['h'i]^"hi".

Note that the latter two methods involve operators with different precedences, which you'll need to take into account. If working around the precedence is too much trouble, you can always fall back on the standard list syntax.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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