Tips for golfing in Pyth

46

13

Pyth is a Python-inspired procedural programming language, created by PPCG user isaacg.

What general tips do you have for golfing in Pyth? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Pyth.

One tip per answer, please.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

Answers

25

Write the code in Python first

Pyth is so similar to Python that it is pretty easy to translate Python programs to Pyth. However, because Pyth is a single-letter-per-command language, it is sometimes hard to write straight Pyth. By writing in Python first, there is less to think about at a time (since Python is a pretty easy language to code in).

Justin

Posted 2014-10-20T00:27:29.713

Reputation: 19 757

@mbomb007 Download the Pyth interpreter and read through the documents. That's the only reliable way I know for writing a Pyth program. – Justin – 2015-02-24T17:57:30.207

@mbomb007 Sorry; that's the way I learned how to write Pyth (looking through the source code). There is basically no documentation on Pyth; you basically have to learn the syntax by trial and error. – Justin – 2015-02-24T18:22:30.863

1

You could also mention that you can still use Python syntax in Pyth, so you can convert parts of the program individually or just use python if necessary. (As you did here)

– FryAmTheEggman – 2014-10-20T15:07:49.960

22

Know Your Variables

Pyth has 3 categories of variables: generic pre-initialized variables, variables pre-initialized based on user input, and variables that implicitly generate an assignment on first use.

Generic variables:

b = "\n"
d = " "
k = ""
G = "abcdefghijklmnopqrstuvwxyz"
H = {}                            # (empty dict)
N = '"'
T = 10
Y = []
Z = 0

Input-initialized variables:

Q = eval(input())
z = input()

Note that these initializations will only be run in a given program if the associated variable is used outside of a string in the code. In addition, the order is Q, then z, if both are used.

Assignment on first use variables:

J and K. If you want to initialize them both to the same value, you can do so with an expression such as KJ0, which is equivalent to the lengthier J0K0.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

18

Use the even newer online interpreter to test your answers.

Note that this is new software, so it may be buggy. Please report any problems to me.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

2Awesome! I couldn't find this with google, just what I needed! – theonlygusti – 2015-04-03T17:31:04.623

12

Strings at the end of the line don't need end quotes. For example:

"Hello, world!

is a completely valid Hello World program.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

Quite obvious really, it is the first google result for "Pyth programming language". Did you create your own page on esolangs? – theonlygusti – 2015-04-03T17:30:02.783

3@theonlygusti Yes, I did. Also, it wasn't the first result last October. – isaacg – 2015-04-03T17:33:13.643

10

Use C for base compression

This is actually undocumented, C on a string is actually not direct chr -> int but instead base 256 -> base 10 (which is the same on one char strings). This is hugely helpful in compressing an int, we can use this script to compress:

sCMjQ256

Take 12345678910, it results in ßÜ> (some unprintables in there).

Also with an array of ints, you can concatenate them, and with large strings by converting to code points and treating as base 128 number.

Another usage of C, thanks @xnor for showing me this, is making an arbitrary large number. The naive way is:

^TT

But we can do one byte better with:

CG

this base 256 deconverts the entire alphabet. Results 156490583352162063278528710879425690470022892627113539022649722 = ~1.56e62.

Maltysen

Posted 2014-10-20T00:27:29.713

Reputation: 25 023

Added to doc now. – isaacg – 2015-07-19T03:54:08.650

9

Use the short functional...err...functions

When the lambda argument to map or reduce just applies one operation to the arguments,, you can use the short forms, M and F. fMx is equivalent to mfdx, and fFx is the same thing as .UfbZx. For instance, say we take a list of numbers as input and output each one incremented. A first approach might be:

mhdQ

However, that can be rewritten as:

hMQ

A similar thing applies to reduce with F. As an example, say there's a challenge to calculate the product of a list of integers. Again, a first try may be:

.U*bZQ

However, with F, that can be shortened to:

*FQ

Shaves off three bytes...not bad!

kirbyfan64sos

Posted 2014-10-20T00:27:29.713

Reputation: 8 730

And you don't need Q, as it is supplemented when the function is missing an input, making it *F – Stan Strum – 2017-09-14T06:01:20.113

8

There is now an online tutorial for Pyth.

Full documentation will be added later.

Maltysen

Posted 2014-10-20T00:27:29.713

Reputation: 25 023

7

Keep your Pyth implementation up to date.

I'm fairly regularly improving Pyth, removing less useful features and adding more useful ones, so keep an eye out for what's new and update your copy of the implementation regularly.

Some recently added features: (as of 10/19/14)

y: Acts as *2 on numbers, and as list of all subsets on strings and lists. For instance:

pyth -c 'y"abc'
['', 'a', 'b', 'c', 'ab', 'ac', 'bc', 'abc']

f: f is normally the filter command. Now, when called with a number as its second argument, it will filter over the infinite sequence starting with that number and counting up by ones, then return the first element of the resulting sequence.

For instance, here's the code to find the smallest prime over a billion:

pyth -c 'f!tPT^T9'
1000000007

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

@isaacg Sorry if this is off-topic, but I am wondering how to use the root operation @ in Fdr1+1@Q2Iq%Qd0d to make a factor calculator. When I try to use it, it defaults to the index meaning instead. Is there any way around this behavior? – StardustGogeta – 2016-05-02T03:04:32.353

Those look like useful additions, but what can you use instead of the old yz? mvdczd can't be the shortest way... – Dennis – 2014-10-20T02:50:01.577

1@Dennis I threw out the old y because I don't think Pyth needs to have multiple super-easily parsed input formats, just one, e.g. the Python format. So, yes, I think mvdczd will have to do, unfortunately. – isaacg – 2014-10-20T02:54:29.740

@Dennis Problem solved, just added this to r's string processing suite. – isaacg – 2014-10-21T16:06:28.800

r looks pretty useful. – Dennis – 2014-10-21T17:07:52.980

5

Named arguments in functions (No longer supported)

Sometimes, default values in functions can be useful for golfing. Pyth actually supports this (much to my surprise). For example:

DC=Z1RZ;C;C5

Will print:

1
5

You can also use J and K to save characters when doing this:

DgJ1K1R+JKg;g2;g2 3

prints:

2
3
5

This is usually useful for recursive algorithms.

This no longer works, but I have left it here in case someone wants to golf using an old version of Pyth.

FryAmTheEggman

Posted 2014-10-20T00:27:29.713

Reputation: 16 206

16Wow - Even I didn't realize Pyth supported this, and I wrote the language! – isaacg – 2015-01-12T10:19:16.680

Unfortunately, this no longer works in more recent versions of Pyth. – isaacg – 2015-05-06T05:41:07.233

Should this tip be deleted like one of the other out-of-date tips? If not, maybe it should be marked with what version(s) this tip applies to. – mbomb007 – 2015-08-11T18:19:30.653

@mbomb007 I've been meaning to find the version, but I've been too lazy. Ill delete it if you think it is better off that way until I do find it. – FryAmTheEggman – 2015-08-12T12:50:22.760

@FryAmTheEggman I think it's up to you, since it says in the title that it's not supported. – mbomb007 – 2015-08-12T17:01:08.397

5

Unpacking 2 element tuples with F

Say you have a 2 element tuple, J = (a, b), and you want r(a,b), for some 2 arity function r.

The naive way to do this is rhJeJ.

The fancy way to do this is r.*J, using the unpack operator.

The really fancy way to do this is rFJ, using the fold operator.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

is it still possible to use .u for that? .u seems to be cumulative reduce now. – Ven – 2016-02-18T14:14:05.540

.u -> .* this was a change that was made a while ago, but was never updated. – isaacg – 2016-02-18T16:26:46.253

4

Look at all of the control flow options

Loops:

F: For loop. Just like Python's.

V: For loop over a range. Neither variable nor range must be given, so 2 characters shorter.

W: While loop. Just like Python's.

#: Infinite while loop. Escape with error or explicit break. Only try ... except feature now in Pyth.

Functions:

D: General define. Just like Python.

L: 1 argument, no assignment function, like Python's lambda, but named. Function name, variable name and return (R) need not be given, so 3 characters shorter.

Functional programming:

f: Filter - select elements of input sequence that return truthy on input lambda.

f: First integer greater than or equal to input which gives truthy filter result.

m: Map - transform elements of input sequence using input lambda.

u: Reduce - fold input sequence on input lambda, initializing accumulator to third argument.

o: Order - older elements of input sequence using input lambda as the key.

Usually, there will be multiple possibilities for any given problem, and only by writing test solutions with each of them can you figure out which is shortest.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

.x can more recently be used for try-except blocks. – mbomb007 – 2015-08-11T18:24:55.217

@mbomb007 is there way to neglect except block, I mean can that be left empty? For ex: .x{some_statments}{except_block - can this be empty}. – Gurupad Mamadapur – 2017-01-02T15:32:33.367

@GurupadMamadapur # ... B can be used this way if you're not inside an expression – isaacg – 2017-01-02T15:56:47.697

4

Use map to generate lists

It's basically an equivalent of the fancy list comprehensions of python. Use an existing list or an range to iterate over and map each value, even if the value doesn't matter.

Two examples:

  • Generate a list of 8 zeros.

    mZ8 instead of *8]Z

  • Generate a list of 5 random numbers between 0 and 9:

    mOT5 instead of V5~Y]OT)

    The second one automatically assigns the list to Y (well actually it appends to Y), but even =YmOTU5 is shorter.

Jakube

Posted 2014-10-20T00:27:29.713

Reputation: 21 462

4

Use the short arithmetic functions

h: Other than returning the first element of a list, it increments a number, e.g. hT evaluates to 11. Shorter than +1T.

t: This decrements a number (other than return the tail of a list), e.g. tT evaluates to 9. Shorter than -T1.

y: This doubles a number, e.g. yT evaluates to 20, shorter than *T2 or +TT.

Jakube

Posted 2014-10-20T00:27:29.713

Reputation: 21 462

4

Implicit Q at EOF

This is a new change, as of today.

Q is the variable which is auto-initialized to the evaluated input. It is implicitly appended to the end of the Pyth program, as many times as is necessary to make the arity work out. To see an example of how to use this for golfing, let's say we want to compute the Collatz function of the input.

A shortest way to write it is like this:

@,/Q2h*3QQ

However, since the Qs are implicit at the end of the file, we can simply write:

@,/Q2h*3

Saving 2 bytes.

Note that functions with non-required arguments will not have those arguments filled in. For instance, c"12 12" will not have an implicit Q, since c only requires 1 argument.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

3

Use reduce to apply a function repeatedly.

Suppose you need to set a variable to some function of itself, and repeat a certain number of times. Take, for instance, the problem of finding the number 100 later in the Collatz Sequence from the input. The shortest way to find the next number in the sequence, if the initial number is Q, is

@,/Q2h*Q3Q

The most obvious way to apply this 100 times and print the result would be

V100=Q@,/Q2h*Q3Q;Q

Loop 100 times, updating the value of Q each time, then end the loop and print Q.

Instead, we can use a reduce function which ignores the sequence variable (H).

u@,/G2h*G3GU100Q

This is 2 characters shorter. It is 3 characters shorter if you are trying to loop as many times as there are elements in a sequence.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

3

Switching two elements in a list

Switching two elements can be quite an expensive task. So here are two approaches you wanna use.

Tmp-variable approach

In preparation we define a list Y and fill it with some numbers. The goal is to switch the second and third element.

=Y[1 3 5 3 6 7)AGH,1 2

We simply assign the tmp variable J = Q[G], do the first list assignment Y[G] = Y[H] and then the second last assignment Y[H] = J. The trick here is to nest the two list assignments, so you don't have to suppress printing and don't have to use refer twice to Y.

J@YGXXYG@YHHJ

instead of

J@YG XYG@YHXYHJ

Translating approach

If the elements, which you want to switch, are unique in the list, use this approach. It's really short. So this time we switch the first and third element (the values 1 and 5 are unique).

=Y[1 3 5 3 6 7)K,Z2

This uses the translation functionality of the list:

XYm@YdK)

This translating replaces every element Y[0] with Y[1] and every Y[1] with Y[0]. So if the values are not unique, bad things happend. For instance K,1 2 results in [1, 5, 3, 5, 6, 7].

Notice that the closing parentheses is optional, if the statement is the last one in your code.

Jakube

Posted 2014-10-20T00:27:29.713

Reputation: 21 462

3

Debugging with <newline>

If your code is written in an imperative programming style, it is quite easy to debug, since you can easily print intermediate results. (permalink)

FN.:Q2                loop
      =Y+-Y-FNhN      some random command
                Y     print intermediate result
                 ;Y   end for loop and print result

But a large amount of Pyth programs use elements of functional programming, like map, filter and reduce, which don't allow such a simple printing. But it is still possible, using the \n command.

The same code using u (reduce) would be: (permalink)

u        .:Q2Y   reduce .:Q2, start with G = Y
 +-G-FHhH        random command

If you want to print the intermediate values, simply to insert \n: (permalink)

u         .:Q2Y   reduce
   \nG             print(G)
 +-\nG-FHhH        random command

\na prints a on a newline and returns a. So you can insert it anywhere without worrying changing the functionality of the program.

Jakube

Posted 2014-10-20T00:27:29.713

Reputation: 21 462

You can nowadays use the newline for this, which also prints a newline. – PurkkaKoodari – 2016-06-04T14:27:30.923

@Pietu1998 Yes, I use it all the time. Time to update the post. – Jakube – 2016-06-04T14:33:01.220

3

There are usually shorter alternatives to Any

When you want to find if any of a sequence satisfy a condition, you would usually use .Em. For example, if you want to find out if any in a list are greater-than-or-equal to 5:

.Emgd5Q

But, if it only needs to be a truthy/falsey, not true/false, sm would work since sum works on bools.

smgd5Q

We can even do one shorter, with filter:

fgT5Q

The last one looks really ugly though.

For .All, the only thing I can think of is to use the opposite condition and negate it for a one char save over .Am:

!f<T5Q

Maltysen

Posted 2014-10-20T00:27:29.713

Reputation: 25 023

3

Finding the maximum of two integers

g#

For instance, suppose you have J=5 and K=12. Then g#JK = 12, and g#KJ = 12 as well.

This was discovered by @Pietu1998, who put it this way:

Not sure if someone has already found it, but there's a cool way to do max(A, B) in 2 bytes, no need to use 3 for eS,AB. g#AB does the same thing. (It's very inefficient, though, since it loops max(1, A-B+1) times. An optimization is to put the number likely to be larger as B.)

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

@Jakube This is true. I apparently misthough something while typing this in chat. – PurkkaKoodari – 2016-06-04T15:01:34.453

2

Pyth's join method

The join method in Python can be often a little bit annoying, since it only joins strings. Pyth's join is more generous. It transforms all objects in strings by default.

E.g. jkUT gives 0123456789 or jb["abc"4,5\f]7 gives

abc
4
(5, 'f')
[7]

Jakube

Posted 2014-10-20T00:27:29.713

Reputation: 21 462

Recently, even more transforming-into-string functionality was added - the first argument is coerced to a string as well, e.g. j2\a\b -> "a2b" – isaacg – 2015-03-27T15:07:49.550

1

Divisibility testing using I and GCD

Disclaimer: This only works for non-negative integers.

To check if two non-negative integers are divisible, you can do the following:

iI<divisor><dividend>

If a is divisible by b and a ≥ b ≥ 0, then gcd(a, b) = b.

It doesn't necessarily save bytes over !%<dividend><divisor>, but it might bring you a saving, because:

  • You might be able to tweak the implicit stuff at the end of a Pyth program (like dropping Q), when working with the dividend.
  • You can use it as a <pfn>, since it is a function on its own.
  • It handles modulo by 0.

Try it!

Mr. Xcoder

Posted 2014-10-20T00:27:29.713

Reputation: 39 774

One more advantage: iI is a function on its own, whereas !% is not, so you can use it as a prefix function. – Erik the Outgolfer – 2017-12-31T14:50:38.193

@EriktheOutgolfer Thanks, added to the list of advantages :) – Mr. Xcoder – 2017-12-31T14:52:06.173

1

Telling if a Number is a Whole Number

A neat trick is using Invariant to tell if a number is a whole number as such:

sI

This checks if the number doesn't change when you truncate it, which it won't if it's a whole number.

For example, you can use this as a perfect square check:

sI@Q2

Maltysen

Posted 2014-10-20T00:27:29.713

Reputation: 25 023

1

Use Packed Pyth

Packed Pyth is a new "programming language" which is exactly the same as Pyth, except that it uses 7 bits per character instead of 8 bits per character.

To use it, clone the pyth repository. The file packed-pyth.py is the interpreter.

Say your code is "Hello, world!.

First, put it in a file: echo -n '"Hello, world!' > code.pyth

Next, pack the Pyth code into Packed Pyth file: python3 packed-pyth.py -p code.pyth code.ppyth

Finally, run the Packed Pyth code: python3 packed-pyth.py code.ppyth

When running code, you can provide the -d flag to see what the Pyth code that's actually being run is, and you can provide input as a second command line argument after the file containing the code.

Upside:

  • Code is shorter by 1/8th.

Downside:

  • ASCII only.

  • No interactive input.

  • Full debug options are not availible.

  • Worse error reporting.

isaacg

Posted 2014-10-20T00:27:29.713

Reputation: 39 268

1like we did for the safe mode thing, can we move it into a flag? – Maltysen – 2016-10-06T02:48:10.880

This is awesome btw :D – Maltysen – 2016-10-06T02:48:18.557

@Maltysen I think that would increase the byte score by one. – isaacg – 2016-10-06T04:28:13.800

Can't Pyth be packed further since it only uses printable ASCII? – lirtosiast – 2018-11-15T07:46:08.310

0

Assigning a variable to a function applied to itself

If you have a function of arity 1, and want to apply that to a variable and apply to itself, you can use the following syntax:

=<function><variable>

Instead of:

=<variable><function><variable>

For example, if you want to increment the variable Z, you can do:

=hZ

Which saves one byte over =ZhZ.

Mr. Xcoder

Posted 2014-10-20T00:27:29.713

Reputation: 39 774