Tips for golfing in Jelly

46

10

Jelly is a tacit, golf-oriented programming language by our very own Dennis. It’s popping up in answers here more and more often, beating other golf-y languages like Pyth and CJam, both by using its own code page and its powerful chain system to succinctly express programs.

Let’s collect some useful tips for golfing in Jelly. (As always, one tip per answer, please!)

Lynn

Posted 2016-02-03T13:21:46.303

Reputation: 55 648

13Makes me wonder if Jelly is still too much in flux for this to generate content that will be useful in the long run, but the best person to answer that is probably Dennis. – Martin Ender – 2016-02-03T13:30:29.070

2I think there are already plenty of tips that should make sense no matter what changes happen in the language. Tips for golfing in Pyth has the same problem, I suppose, but it has passed the test of time reasonably well so far; answers are usually updated whenever they no longer apply because of a language change, albeit with some delay. – Lynn – 2016-02-03T13:32:45.547

3Here's a nice tip: become @Dennis's apprentice. Then, you will really be good at golfing jelly. – Conor O'Brien – 2016-02-03T19:41:31.050

12@Lynn I love how you say our very own Dennis. It's like we're all one big family :D. – None – 2016-03-19T17:11:26.923

This thread in Jelly's GitHub issues on how to actually type Jelly code page characters is probably worth a tip or two: https://github.com/DennisMitchell/jelly/issues/6 I don't have access to a Windows machine, though, so I don't feel confident writing up those bits.

– Jordan – 2017-11-02T15:47:04.327

Answers

26

String compression

If you're looking for a more optimized/automatic string compressor, try this one.

Try it online!

A compressed string looks like “...», where the dots are a chunk of base-250-encoded data. The decompression algorithm is a bit complicated: the chunk is interpreted as a “mixed-base” integer, with divmod breaking off various parts of this integer and constructing a string out of them.

I’ve created a little Python 3 interface to compress Jelly strings:

import dictionary
code_page = '''¡¢£¤¥¦©¬®µ½¿€ÆÇÐÑ×ØŒÞßæçðıȷñ÷øœþ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~¶°¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ƁƇƊƑƓƘⱮƝƤƬƲȤɓƈɗƒɠɦƙɱɲƥʠɼʂƭʋȥẠḄḌẸḤỊḲḶṂṆỌṚṢṬỤṾẈỴẒȦḂĊḊĖḞĠḢİĿṀṄȮṖṘṠṪẆẊẎŻạḅḍẹḥịḳḷṃṇọṛṣṭụṿẉỵẓȧḃċḋėḟġḣŀṁṅȯṗṙṡṫẇẋẏż«»‘’“”'''

class Compress(list):
    def character(self, c):
        if c in '\n\x7f¶':
            o = 95
        elif ' ' <= c <= '~':
            o = ord(c)-32
        else:
            raise ValueError(c + " is neither printable ASCII nor a linefeed.")
        self += [lambda z: 3*z+0, lambda z: 96*z+o]; return self
    def string(self, s):
        for c in s: self.character(c)
        return self
    def dictionary(self, w):
        ts = bool(self)
        if w[:1] == ' ': w = w[1:]; ts = not ts
        dct = dictionary.short if len(w) < 6 else dictionary.long
        W, sc = (w, 0) if w in dct else (w[:1].swapcase() + w[1:], 1)
        if W not in dct: raise ValueError(w + " isn't in the dictionary.")
        f = ts or sc; j = (2 if sc else 1) if ts else 0; i = dct.index(W)
        self += [lambda z: 3*z+2, lambda z: 3*z+j] if f else [lambda z: 3*z+1]
        self += [lambda z: 2*z+int(len(w) < 6), lambda z: len(dct)*z+i]
        return self
    def go(self):
        compressed = []; z = 0
        for f in self[::-1]: z = f(z)
        while z:
            c = z % 250
            if c == 0: c = 250
            z = (z - c) // 250
            compressed.append(code_page[c - 1])
        return '“{0}»'.format(''.join(compressed[::-1]))

Use the compressor as follows.

print(Compress()
      .dictionary('public')
      .dictionary(' static')
      .dictionary(' boolean')
      .string(' is')
      .dictionary('Power')
      .string('Of')
      .dictionary('Ten')
      .string('(')
      .dictionary('long')
      .dictionary(' input')
      .string(') {\n ')
      .dictionary(' return')
      .string('\n   ')
      .dictionary(' input')
      .string(' ==')
      .go())

Compress is a string builder:

  • .string(s) will insert raw, printable ASCII characters into the string.

    (Each character costs about 0.827 compressed bytes.)

  • .dictionary(w) will look up a string in Jelly’s built-in dictionaries. You may begin the string with a single space, if you want one. If this needs to deviate from the normal string-adding behavior, or flip the capitalization of a dictionary word, it’ll add flags accordingly.

    (Costs about 1.997 bytes for short words, 2.433 bytes for long words; if there are flags, add 0.199 bytes.)

Lynn

Posted 2016-02-03T13:21:46.303

Reputation: 55 648

15That's useful! I grew a little tired of compressing these by hand... – Dennis – 2016-02-03T15:56:12.113

1@Dennis Could this be included in Jelly itself, so we wouldn't have to copy it from here every time? – PurkkaKoodari – 2016-10-24T18:16:48.340

@Pietu1998 As an atom or something else? – Dennis – 2016-10-24T18:26:06.187

@Dennis As a utility script in the repository. – PurkkaKoodari – 2016-10-24T18:26:52.023

@Dennis: It crosses my mind that it'd make sense for to compress its output in this way if doing so is shorter than outputting a "normal" string. – None – 2016-12-05T01:52:05.883

Note: On mac, you'll need to import sys. You'll also need to replace the print statement to output the compressed text with sys.stdout.buffer.write(Compress().go().encode(utf-8). – Rɪᴋᴇʀ – 2017-05-05T19:31:31.313

This is giving me issues; it says module 'dictionary' has no attribute 'short'. – HyperNeutrino – 2017-05-12T03:39:27.863

1Probably requires something in Jelly. – CalculatorFeline – 2017-06-17T23:49:35.640

20

This is part of what became the Jelly wiki tutorial.

Chains

(This is sort of a follow-up to Tacit programming.)

How does Jelly evaluate a chain? As explained before, there are three cases to consider: whether this chain was called niladically, monadically, or dyadically.


1. Niladic chains

These are the easiest of the bunch. To evaluate a niladic chain that starts with a nilad, like α f g h, evaluate the monadic chain f g h at that nilad α. (Caveats: if the whole chain is empty, 0 is returned instead. If α isn’t a nilad, replace use α=0 instead.)

For example, is just ½ evaluated at 4, which is 2.


2. Monadic chains

Monadic chains are broken down from left to right, until there are no links left to consider. Also, we’re passed some argument ω here. There are two questions to answer:

What’s the starting value for this left-to-right evaluation?

  • If our chain starts with a nilad α, and is followed by zero or more monads (like ½), dyad-nilad pairs (like +2), and nilad-dyad pairs (like 4*): we start by evaluating α, and then consider the rest of the chain.

  • Otherwise, we start from the argument passed to this chain, ω, and consider the entire chain.

How do we walk down the chain?

Let’s call V the current value – initially, it’s the value described above, but it gets update as we go through the chain – and denote

  • nilads using digits,
  • monads using lowercase letters,
  • dyads using operator symbols +, ×, ÷.

Then the following patterns are matched against, from top to bottom:

                                 ┌───────────┬─────────┐
                                 │ old chain │ new V   │
                                 ╞═══════════╪═════════╡
                                 │ + × 1 ... │ (V+ω)×1 │ *
                                 │ + f ...   │ V+f(ω)  │
                                 │ + 1 ...   │ V+1     │
                                 │ 1 + ...   │ 1+V     │
                                 │ + ...     │ V+ω     │
                                 │ f ...     │ f(V)    │
                                 └───────────┴─────────┘
      (* Only if `...` consists of monads, dyad-nilad pairs, and nilad-dyad pairs.)

Let’s try this out on the chain +²×.

  • + isn’t a nilad, so we start out at V = ω.
  • Then, we chop off , matching the second pattern, and get V = ω+ω².
  • Then, we chop off ×, matching the fifth pattern, and get V = (ω+ω²)×ω.
  • The chain is now empty, so (ω+ω²)×ω is our final result.

3. Dyadic chains

These are basically like monadic chains, but this time, there are two arguments, λ (left) and ρ (right).

What’s the starting value?

  • If the chain starts with three dyads like + × %, we start at λ+ρ, and consider the chain × % ... next.

  • Otherwise, we start from λ, and consider the entire chain.

How do we walk down the chain?

This time, the patterns are

                                 ┌───────────┬─────────┐
                                 │ old chain │ new V   │
                                 ╞═══════════╪═════════╡
                                 │ + × 1 ... │ (V+ρ)×1 │ *
                                 │ + × ...   │ V+(λ×ρ) │
                                 │ + 1 ...   │ V+1     │
                                 │ 1 + ...   │ 1+V     │
                                 │ + ...     │ V+ρ     │
                                 │ f ...     │ f(V)    │
                                 └───────────┴─────────┘
      (* Only if `...` consists of monads, dyad-nilad pairs, and nilad-dyad pairs.)

Let’s try this out on the chain +×÷½.

  • The chain starts with three dyads, so we start at V = λ+ρ, and throw away the +.
  • Then, we chop off ×÷, matching the second pattern, and get V = (λ+ρ)×(λ÷ρ).
  • Then, we chop off ½, matching the sixth pattern, and get V = sqrt((λ+ρ)×(λ÷ρ)).
  • The chain is now empty, so we’re done.

Lynn

Posted 2016-02-03T13:21:46.303

Reputation: 55 648

2So is this becoming more of a Jelly tutorial than tips for golfing in it? ;) – Martin Ender – 2016-02-04T07:59:56.353

5I suppose so. If the language is very tricky to understand, I think “how does any of this even work?!” is a good tip ^^ I do intend to eventually move on to non-obvious tricks and ways to save bytes. If this is out of place here, I could move it to GitHub, or (if Dennis likes it) the Jelly repo. – Lynn – 2016-02-04T12:06:01.547

16

Special-cased numeric values

Here are some special cases for Jelly's numerics parser:

  • - evaluates to -1
  • . evaluates to 0.5
  • ȷ evaluates to 1000 (ȷ is for scientific notation, e.g. 2ȷ6 is 2000000)
  • ı evalulates to 1j (ı is for complex numbers, e.g. 2ı3 is 2+3j)

It's also worth noting that something like is actually 4+1j, rather than 4.

You can mix and match these, e.g.:

  • -. is -0.5 and is -1000
  • is -1+1j, ı- is -1j and -ı- is -1-1j
  • is 500.0
  • is 0.5+1j, ı. is 0.5j and .ı. is 0.5+0.5j
  • ȷı is 1000+1j, ıȷ is 1000j and ȷıȷ is 1000+1000j

Note that ȷ- is 0.1, but that doesn't save any bytes over .1. Then there's also the following, which can already be done in the corresponding number of bytes by using the builtin variable for 10 (), but might be useful in the rare case that the builtin is unavailable or to save on needing to use ¤:

  • ȷ. is sqrt(10) ~ 3.162277, .ȷ. is sqrt(10)/2 ~ 1.5811 and ȷ-. is 1/sqrt(10) ~ 0.31162

Sp3000

Posted 2016-02-03T13:21:46.303

Reputation: 58 729

13

Optimised string compressor

Try it online!

Lynn's post details what compressed strings are exactly, along with having a compressor that will produce these compressed strings. However, while tinkering around with a program in Jelly, I found it tiring to have to combine .dictionary and .string along with the correct placements of spaces and so on and so forth, in order to achieve the shortest possible string.

Therefore I decided to create an extension to Lynn's script that would take user input, and find the shortest way it can be compressed without the user having to do any work. The script is quite long, and so I've added in a TIO link, rather than the code itself.

The way the program works is by compressing using 3 different methods and determining which is shortest:

  1. Method 1 simply encodes each byte of the input at a time, which tends to produce the longest result, as compared to the others. Even when the input is very short, it still manages to create rather long alternatives. For example, the shortest way that test can be compressed is “¡ḌY», whereas this method returns “¡⁷ƑKʂ» (2 bytes longer). As a general rule, this only works if the string is less than 4 characters long

  2. The second method breaks the string into words and punctuation, as the two are added to the compressor in different ways. Words that are part of the dictionary are added with the .dictionary method of Lynn's code, which compresses them more than if they were simply added by code points. Punctuation however has to be added by code point as, unfortunately, they aren't part of the dictionary.

    Punctuation includes spaces, which is where method number 3 comes into play, but first and evaluation of method 2: let's compare methods one and two compressing the string Hello, World! (contains words, punctuation and spaces, so is perfect). Compressing character by character results in the final string of “ŒCdẉa÷¹ṂȤƓ(Ẋ)» (15 bytes long), which, as it turns out, is longer than the easier way to output Hello, World!: “Hello, World!. Now lets take a look at method two. This produces the compressed string “Ọƥ⁷Ƭė3⁶» which weighs in at 9 bytes, a great improvement over the old one. However, the shortest Hello, World! program in Jelly is 8 bytes, so something can be improved

  3. Here comes method 3, making it even shorter. As expected, output for Hello, World is, of course, “3ḅaė;œ» which is the shortest possible program. So what does method 3 do, that method 2 doesn't? Method 3 combines lone spaces into leading spaces, which the Jelly decompressor has a flag for. In the code for both the compressor and the decompressor, you can see code like if flag_space: word = ' ' + word, showing that leading spaces are a) supported and b) byte-saving. Therefore, the string splitter from method two is adapted so that spaces by themselves are combined into the string directly after is, to create leading strings. This means that Hello, World! is parsed as ["Hello", ",", " World", "!"], which when compressed is only 6 bytes (8 when including delimiters). This is almost always the shortest method of compression, excluding the "extension" I added,

This is the bulk of the program, but there are a few more options that help it compress data even more.

  • The program checks whether or not each compressed version is correct, by using Dennis' sss decompressor that Jelly uses (go straight to the source)
  • You can see all different compressed strings by making the first command line argument --debug, which, rather than simply showing the shortest compressed string, shows all 3 along with a "This is the shortest" copy
  • The program handles "non-words"

Non-words

I started work on Lynn's compressor after seeing this message, took a crack at it and got frustrated at the fact that I couldn't find the shortest way to compress it (it's 29 or 32 bytes for the record). However, while testing my improvements, I found that words such as knowns aren't in Jelly's dictionary. Therefore, I set out to find a way to compress these "non-words" in the shortest Jelly code possible.

I created a function (trim) that splits the string as a point where at least one of the parts of the string are words. For example knowns would be split into ["known", "s"] and have the program add the first word via a dictionary add (.dictionary) and the second part of the word via a .string call. But this still leaves two edge cases: strings which have no words in them (such as ajdl) and non-words which have words at the end, such as abctest, which wouldn't get split by the trim function.

As there is no way of finding words in a string which has no words in it, the simplest and shortest way to handle these is by adding them in character by character via a .string call. So ajdl would get added by .string('ajdl'). Whereas non-words which end with recognised words, essentially implements the trimmer but in reverse, and applying .dictionary and .string the other way round to the forwards trimmer.

As it turns out, trimming the string, either from the start or the end is, of course, shorter than adding each character to the compressor, as demonstrated by an input of abctest this string, which produces a debug output of

Original        : abctest this string

Optimised       : “¡J+v(p⁸ụƘ$,»
Seperate spaces : “Ç⁴ṭḍµḄ7oeṂdḷp»
No trimmer      : “¤ɦ?Ɓ¢#fḲOạ⁾¶ɼȥƬ»
All characters  : “µẓþ"Y7_ḣṗḢ))9Þ⁴⁺Ẉ²)ɱ»
Non-compressed  : “abctest this string
Shortest        : “¡J+v(p⁸ụƘ$,»
=====

The different between the optimal output (which uses the trimmer) and the one which doesn't is a whopping (for Jelly) 4 bytes. Finally, there are occasions where the string itself is shorter than any compressed version, which has now been factored in.

Of course, a lot of credit for this goes to Lynn for creating the original compressor

caird coinheringaahing

Posted 2016-02-03T13:21:46.303

Reputation: 13 702

Should this be deleted now that we have https://codegolf.stackexchange.com/a/151721/39328?

– lirtosiast – 2018-11-16T06:15:37.507

@lirtosiast By that same argument, you could say that this post should be deleted. Its doing no harm, and is a perfectly valid answer, there is no reason to delete it, simply because there is a better answer.

– caird coinheringaahing – 2018-11-16T17:59:52.370

10

You can use superscript three to nine (³⁴⁵⁶⁷⁸⁹) to golf some usually used values, but this depends on the amount of command line arguments, and in case of links, on the arguments of the links.

  • ³ returns 100, and works only if there's no input.
  • returns 16, and works only if there's at most one input.
  • returns 10, and works only if there's at most two inputs.
  • returns a space if there's at most three inputs.
  • returns a new line if there's at most four inputs.

If there are five inputs, however, you're out of luck.

Recently, a new version of the language lowered the value of ³ to 100, and introduced some new atoms that return values or (for links) their arguments.

  • returns a blank list everywhere except links which have a left argument passed to them.
  • returns 256 everywhere except links which have a right argument passed to them.

If you're in a link, and have arguments from both sides passed to it, however, you're out of luck.

user48538

Posted 2016-02-03T13:21:46.303

Reputation: 1 478

1Actually, unused inputs are filled with default values! Such convenience! – CalculatorFeline – 2016-03-09T00:53:18.383

9

Abuse string bugs

Credits go to Adnan for taking advantage of this first in Write a program to elasticize strings.

Jelly is supposed to get character arithmetic one day, but until that happens, we can take advantage of the fact that Python overloads most arithmetic operators and that Jelly does no type checking.

For example

“abcd”Ḥ

isn't supposed to do anything useful right now, but since (unhalve) is implemented as

lambda z: z * 2

and arithmetic atoms vectorize at depth 0 (i.e., they operate on numbers or characters), the above Jelly code yields

['aa', 'bb', 'cc', 'dd']

Careful that this produces actual Python strings (a type Jelly isn't supposed to have), so this won't be usable in all situations.

Likewise, +/ can be useful to concatenate strings, with the same caveats.

Dennis

Posted 2016-02-03T13:21:46.303

Reputation: 196 637

So if in the future when character arithmetic is added, will you add type overloads for strings and other non-numeric types to those atoms? – miles – 2016-10-20T04:05:47.000

1More or less. I'm planning to create a numeric type that is internally a number but has a character flag. That flag would only affect printing; the characters could be used instead of integers everywhere. – Dennis – 2016-10-20T04:13:29.693

1S can never be useful to concatenate strings, it will attempt to add 0 to them. – Erik the Outgolfer – 2017-06-22T11:37:39.137

@EriktheOutgolfer Right, pesky base case. +/ works though. – Dennis – 2017-06-22T12:33:13.477

@Dennis Yeah, I've pretty much won a challenge as of now with +/. – Erik the Outgolfer – 2017-06-22T12:35:14.367

Why can't you just do ;/ to concatenate strings, in all cases? – scatter – 2017-06-22T12:36:47.340

;/ concatenates rows, +/ concatenates columns. – Dennis – 2017-06-22T12:37:25.283

@Dennis Also as of a few days ago replaces ;/. – Erik the Outgolfer – 2017-06-22T12:39:08.357

8

Optimal string compressor

Recently I asked Erik the Outgolfer to add the optimized string compressor to JHT references page, but they said that

sorry, but that compressor doesn't seem to be fully implemented
it says “ugtestslug” is the shortest possible for ugtestslug, while “#ṀȮụḄPƇ» also does the job

So I decide to implement the optimal string compressor.

Simple approach, but guarantee to find the smallest possible value (and hence byte count)

Take input from stdin, output to stdout. Just like the original compressor, or (literal newline character) can be entered as newline.

Trying to run it with a lot of punctuation (for example, input ¶-----¶) will output the uncompressed string.

Of course, a lot of credit for this goes to Lynn for creating the original compressor.

user202729

Posted 2016-02-03T13:21:46.303

Reputation: 14 620

7

This is part of what became the Jelly wiki tutorial.

Tacit programming

Jelly is a tacit programming language. This means you define links (functions) by composing existing links into a chain, without explicitly talking about the arguments involved. Which way the arguments “flow” through this composition is defined by the pattern the links are arranged in. An example of this will be given soon, but first we’ll need to introduce some concepts.

The arity of a link is a very crucial concept. All of the atoms – the built-ins, like + and ½ – have fixed arities. Links are sorted into three categories, depending on their arity:

  • Nilads take no arguments (arity 0); other than some I/O and stateful commands, they mostly represent constant values. For example, the literal 3 is a nilad.

  • Monads take one argument (arity 1). (There’s no connection to functional programming monads here.) For example, ½ (square root) is a monad.

  • Dyads take two arguments (arity 2): a left and a right argument. For example, + is a dyad.

(Using adjectives, we say that a link is niladic, monadic, or dyadic.)

So what’s the arity of the links we define when writing a program? By default, they are variadic – that is, it’s up to the caller to specify how many arguments to use, and in the case of the main link, it depends on how many arguments the program is passed.

As an example, is a chain of + (addition) and ½ (square root). As the respective arities of the elements of this chain are 2 and 1, we call it a 2,1-chain. The interpreter has specific rules for breaking down chains, based on their arities: those rules dictate that, given an input n, this new link computes n + sqrt(n). (You can read as “... plus its square root.”)

Jelly programming, then, is essentially the art of learning these rules well, and composing clever chains that get the job done, tacitly.

Lynn

Posted 2016-02-03T13:21:46.303

Reputation: 55 648

Are there triads? – Conor O'Brien – 2016-02-29T16:54:14.793

Nope! I’m curious to see how Dennis will implement, say, string replacement, which is a bit inherently triadic (replace(str, old, new)). – Lynn – 2016-02-29T17:28:33.990

I don't know Jelly, but I do know a bit of J. Maybe it could be, say, string (operator) (list), where (list) is a binary list old, new. That would make sense, jelly having a built-in pair operator. Under that scheme, though, it would be two bytes for the operator. – Conor O'Brien – 2016-02-29T17:44:18.147

5Wait... ½ is square root? Why isn't ½, um... half? Why not for square root? :( – Cyoce – 2016-04-05T17:20:55.877

@Cyoce because H is already half, obviously :P – Ven – 2016-04-05T21:03:50.947

@CᴏɴᴏʀO'Bʀɪᴇɴ that brings up an interesting point: how does Jelly do the conditional (ternary) operation? The pair trick can't work, because conditionals should be lazily-evaluated to be able to govern control flow as well. – Cyoce – 2016-04-05T22:57:08.580

@Cyoce using ? – Ven – 2016-05-19T16:50:36.423

6

This is part of what became the Jelly wiki tutorial.

Program structure

Each line in a Jelly program is a link definition. Links are basically functions. The bottom line represents “main”: it's the link that gets evaluated using the arguments passed on the command line.

All links but the last one, then, are function definitions: you can refer to them using actors. For example, ç is “the link above this one, as a binary operator (dyad)”. Consider this example program, which computes the square of the sum of its arguments:

+
ç²

This is sort of like the pseudocode:

define f:
    the built-in link +
define main:
    apply the dyad f
    square the result

Lynn

Posted 2016-02-03T13:21:46.303

Reputation: 55 648

6

This is part of what became the Jelly wiki tutorial.

Multi-chain links

Remember when I wrote that you define a link by making a chain of other links? I wasn’t telling the whole truth: in reality, it’s a two-layer process. A link is a chain of chains, and by default, the outer chain simply has unit length.

Consider this program:

C+H

That’s complement plus half. It takes an input value n and calculates (1-n)+(n/2). Not too exciting, I know. But the structure is really like this:

                                                    structure 1

The link we wrote is, itself, actually a chain containing a single chain.

Suppose that we want to calculate (1-n)+(1-n)(n/2) instead. The dyadic chain would work: by the chaining rules, it calculates λ+(λ×ρ), which looks a lot like what we need. However, simply replacing + by in our program won’t do: C+×H is a 1,2,2,1-chain – complement, then add (the argument), then multiply by half – computing ((1-n)+n)×(n/2).

We want Jelly to treat as a unit, and make a 1,2,1-chain of the sub-chains C, , and H. Multi-chain links let us do just that! To construct them, we use the chain separators øµð: in the image above, they would introduce a new blue rectangle, of arity 0, 1 and 2, respectively. In our case, we can group the chains the way we want by writing Cð+×µH:

                            enter image description here

There’s no way to nest these things even further. You’ll have to define multiple links, instead.

Lynn

Posted 2016-02-03T13:21:46.303

Reputation: 55 648

How did you make those charts, out of curiosity? – Conor O'Brien – 2016-04-05T23:42:39.520

4I drew them by hand in Paint.NET :) – Lynn – 2016-04-06T16:06:20.040

4Wow! Impressive! This would be cool for hard Jelly programs, like a tool that does this. (Akin to HexagonyColorer or whatever it's called) – Conor O'Brien – 2016-04-06T16:14:24.870

5

If TMTOWTDI, pick the one that fits your chain.

One of the advantages of a tacit language is that you usually can get away without using variable references. However, this works only if the links in your chain have the right arities.

For example, the straightforward way of taking the sum of all arrays in a 2D array is

S€

which maps the sum atom over all elements of the array.

Now say you have a monadic chain that consists of the atom

*

which maps each x of a 2D array to xx. For example, for A = [[1, 2], [3, 1], [2, 3]], calling the chain would yield [[1, 4], [27, 1], [4, 27]].

Now, we want to take the sum of each pair. Unfortunately,

*S€

doesn't work since * doesn't act like a hook anymore (using A itself as right argument), but as a fork, meaning that S€ gets applied to A first, and the result is the right argument of *.

Fixing this is easy enough:

*¹S€
*⁸S€

Both produce the desired result: is a fork where ¹ is the identity function, and *⁸ is an atop, where is a reference to the chain's left argument (A).

However, there's a way to save a byte! The atop ḅ1 (convert from unary to integer) also computes the sum of each array in AA, but unlike S€, is a dyadic link.

The chain

*ḅ1

returns [5, 28, 31] (as desired); since is dyadic, * hooks instead of forking

Dennis

Posted 2016-02-03T13:21:46.303

Reputation: 196 637

"The atop ḅ1"? – CalculatorFeline – 2017-02-25T19:41:02.993

Atop is J terminology. In Jelly, it is a type of chain that operates directly on the last return value, so it is executed on top of the previous link of the chain. – Dennis – 2017-02-25T19:49:26.810

Another reason to never try to learn this language...:P – CalculatorFeline – 2017-02-25T19:52:52.157

1I felt the same way about J/APL, but after a while it feels as natural as any other language. – Dennis – 2017-02-25T19:54:15.550

4

Integer compression

String compression is useful when producing text in English, but if you need to compress other sorts of data, it's fairly ineffective. As such, most of the time you want to store a large fixed constant in your program, it's best to store it as an integer.

Now that Jelly has its own code page as a constant, the compression algorithm for integers is most simply expressed in Jelly itself:

ḃ250ịØJ”“;;”’ṄV

Try it online!

(The above program also contains a check to show the value that the integer decompresses to.)

In addition to just using an integer as an integer, you can also use it to create a string via doing base conversion on it, then indexing into an alphabet of characters. The atom automates this process, and is fairly useful because it can describe the entire process of decompression (other than the alphabet being decompressed into) in a single byte.

user62131

Posted 2016-02-03T13:21:46.303

Reputation:

4

Use outer products to create useful integer matrices

Here is a gist I have created of this post with slightly nicer HTML table formatting.

The outer product quick þ can be attached to dyads and causes the dyad to act on each pair of elements in its left and right arguments. It is shorthand for €Ð€. For example if we had the code [1,2]+€Ð€[0,10] we could shorten it to [1,2]+þ[0,10] and they would both yield [[1,2],[11,12]]. I will refer to a dyad with þ applied (such as ) as an outer product dyad.

When an integer is one of the arguments of an outer product dyad, Jelly takes the range of that number first then uses the result as the argument. Knowing this, the above example can be further shortened to 2+þ[0,10]. This is true for both the left and right arguments of an outer product dyad.

Some outer product dyads when acting monadically on an integer yield certain integer matrices that can be useful in golfing (especially ASCII art challenges) but would take many bytes to otherwise construct. For example when applied to an integer n yields an n×n identity matrix. Try online!

Below is a table of dyads and the type of matrices they yield when turned into outer product dyads and acted monadically on an integer. Dyads listed in the same row will yield the same matrices. There are dyads I haven't included in the table like &, |, %, w, and that also produce integer matrices but their patterns are not as simple and would likely be useful in fewer situations. Try online!

+-----------------------------------------------------+
|  Dyad  |        Resulting matrix        |  Example  |
+-----------------------------------------------------+
|   = ⁼  |                                |   1 0 0   |
|   ċ    |        Identity matrix         |   0 1 0   |
|        |                                |   0 0 1   |
+-----------------------------------------------------+
|        | Elements above diagonal are 1, |   0 1 1   |
|   <    | all other elements are 0       |   0 0 1   |
|        |                                |   0 0 0   |
+-----------------------------------------------------+
|        | Elements below diagonal are 1, |   0 0 0   |
|   >    | all other elements are 0       |   1 0 0   |
|        |                                |   1 1 0   |
+-----------------------------------------------------+
|        | Diagonal elements are 0,       |   0 1 1   |
|   n ⁻  | off diagonal elements are 1    |   1 0 1   |
|        |                                |   1 1 0   |
+-----------------------------------------------------+
|   a ȧ  |                                |   1 1 1   |
|   ṛ ị  |    Row index of each element   |   2 2 2   |
|        |                                |   3 3 3   |
+-----------------------------------------------------+
|   o ḷ  |                                |   1 2 3   |
|   ȯ    |  Column index of each element  |   1 2 3   |
|        |                                |   1 2 3   |
+-----------------------------------------------------+
|        |  Main diagonal is 0, upper     |  0  1  2  |
|   _    |  diagonals are 1, 2..., lower  | -1  0  1  |
|        |  diagonals are -1, -2...       | -2 -1  0  |
+-----------------------------------------------------+
|        |  Main diagonal is 0, lower     |  0 -1 -2  |
|   _@   |  diagonals are 1, 2..., upper  |  1  0 -1  |
|        |  diagonals are -1, -2...       |  2  1  0  |
+-----------------------------------------------------+
|        |  Main diagonal is 0, upper     |   0 1 2   |
|   ạ    |  and lower diagonals are 1,    |   1 0 1   |
|        |  2, 3...                       |   2 1 0   |
+-----------------------------------------------------+
|        |                                |   2 3 4   |
|   +    |  Row index plus column index   |   3 4 5   |
|        |                                |   4 5 6   |
+-----------------------------------------------------+
|        |       Minimum of row           |   1 1 1   |
|   «    |       and column indices       |   1 2 2   |
|        |                                |   1 2 3   |
+-----------------------------------------------------+
|        |       Maximum of row           |   1 2 3   |
|   »    |       and column indices       |   2 2 3   |
|        |                                |   3 3 3   |
+-----------------------------------------------------+

dylnan

Posted 2016-02-03T13:21:46.303

Reputation: 4 993

3

There are several nonobvious ways to check properties of an argument using Ƒ. Below are a few. I left out plenty of uses of this quick (E.g. , ŒuƑ, ) because they are already the most straightforward methods of achieving their behavior.

OƑ  Is number?
ỌƑ  Is character? (errors on negative numeric input)
ḂƑ  Between 0 and 2? 0<=x<2 (python). <2aAƑƊ or of course ⁼Ḃ$ in Jelly.
ḞƑ  Is integer?
UƑ  Like `ŒḂ`, but checks if all sublists of depth 1 are palindromes.
ṠƑ  Is one of -1, 0, 1? (e-r1¤$)

Feel free to edit this to add more interesting cases.

dylnan

Posted 2016-02-03T13:21:46.303

Reputation: 4 993

3

You may want to try an online editor Jelly Balls designed to easily build code in Jelly Language.

The features include:

  • Command palette with all atoms and syntax characters organized by type
  • Online parser recognizing literals and 2-byte atoms in the code
  • Simplified in-browser Jelly interpreter to run your Jelly code on the webpage in javascript
  • Direct links for transferring the code into TIO or another Jelly Balls session
  • Automatic hints
  • Optimised for mobile devices

Give it a try: https://jellyballs.github.io

Jelly Balls

  • Detailed trace report showing arguments and results of every executed step

Jelly Balls

  • Code report showing description of every step

Jelly Balls

  • Recipes page with Jelly examples

Jelly Balls

  • Interactive code page of 256 Jelly characters

Jelly Balls

Jelly Balls

Posted 2016-02-03T13:21:46.303

Reputation: 59

3

Yepp, would better fit Where to find an online testing environment for specific programming languages?.

– manatwork – 2019-05-19T10:56:47.717

4I think a link to an IDE that lets you write Jelly without a custom keyboard layout is a rather useful tip. Many Jelly beginners have struggled with that. – Dennis – 2019-05-19T14:48:05.670

2

List commands and literals

If you attempt to use many of the non-vectorizing list commands on a literal n or a list of literals z, the list command will first convert to a list of some sort and then carry out the command on that list.

These commands appear use calls to the iterable function in jelly.py.

def iterable(argument, make_copy = False, make_digits = False, make_range = False):
    the_type = type(argument)
    if the_type == list:
        return copy.deepcopy(argument) if make_copy else argument
    if the_type != str and make_digits:
        return to_base(argument, 10)
    if the_type != str and make_range:
        return list(range(1, int(argument) + 1))
    return [argument]

Here are some incomplete lists of what those list commands will do.

Wraps in a list

The simplest return from iterable to wrap the argument in a list, and return that to be processed by the function. This happens if the argument is not already a list, is a string, and iterable's arguments don't call for other methods.

-------------------------------------------------------------------------------
| Command | Description     | Process                       | Effect          |
-------------------------------------------------------------------------------
| F       | Flattens a list | 4953F -> [4953]F -> [4953]    | Same as W       |
-------------------------------------------------------------------------------
| G       | Format a list   | 4953G -> [4953]G -> [4953]    | Same as W       |
|         | as a grid       |                               |                 |
-------------------------------------------------------------------------------
| I       | Increments      | 4953I -> [4953]I -> <nothing> | Empty list      |
-------------------------------------------------------------------------------
| S       | Sums a list     | 4953S -> [4953]S -> 4953      | Same as ¹       |
-------------------------------------------------------------------------------
| Ṭ       | Boolean array,  | 4Ṭ -> [4]Ṭ -> [0, 0, 0, 1]    | n-1 zeroes,     |
|         | 1s at indices   |                               | 1 at end        |
-------------------------------------------------------------------------------
| Ụ       | Sort indices by | 4Ụ -> [4]Ụ -> [1]             | Yields [1]      |
|         | by their values |                               |                 |
-------------------------------------------------------------------------------
| Ė       | Enumerate list  | 4Ė -> [4]Ė -> [[1, 4]]        | Yields [[1, n]] |
-------------------------------------------------------------------------------
| Ġ       | Group indices   | 4Ġ -> [4]Ġ -> [[1]]           | Yields [[1]]    |
|         | by values       |                               |                 |
-------------------------------------------------------------------------------
| Œr      | Run-length      | 4Œr -> [4]Œr -> [[4, 1]]      | Yields [[n, 1]] |
|         | encode a list   |                               |                 |
-------------------------------------------------------------------------------

Convert to base 10

The functions here call iterable to converts to a number to a list of its digits D, and then run on those digits.

-------------------------------------------------------------------------
| Command | Description     | Process                      | Effect     |
-------------------------------------------------------------------------
| Q       | Unique elements | 299Q -> [2, 9, 9]Q -> [2, 9] | Unique     |
|         | ordered by      |                              | digits     |
|         | appearance      |                              | of n       |
-------------------------------------------------------------------------
| Ṛ       | Non-vectorized  | 4953Ṣ -> [4, 9, 5, 3]Ṛ       | Reverses D |
|         | reverse         | -> [3, 5, 4, 9]              |            |
-------------------------------------------------------------------------
| Ṣ       | Sort a list     | 4953Ṣ -> [4, 9, 5, 3]Ṣ       | Sorts D    |
|         |                 | -> [3, 4, 5, 9]              |            |
-------------------------------------------------------------------------

Convert to list with range

The functions here convert a number to the range R = [1 ... n], and then run on that range.

-----------------------------------------------------------------------------------------
| Command | Description       | Process                             | Effect            |
-----------------------------------------------------------------------------------------
| X       | Random element    | 4R -> [1 ... 4]X -> 2               | Random element    |
|         |                   |                                     |  of R             |
|         |                   |                                     |                   |
-----------------------------------------------------------------------------------------
| Ḋ       | Dequeue from list | 4R -> [1 ... 4]Ḋ -> [2, 3, 4]       | Range [2 ... n]   |
-----------------------------------------------------------------------------------------
| Ṗ       | Pop from list     | 4Ṗ -> [1 ... 4]Ṗ -> [1, 2, 3]       | Range [1 ... n-1] |
-----------------------------------------------------------------------------------------
| Ẇ       | Sublists of list  | 4Ẇ -> [1 ... 4]Ẇ                    | All sublists of R |
|         |                   | -> [[1], [2], [3], [4], [1, 2],     |                   |
|         |                   |     [2, 3], [3, 4], [1, 2, 3],      |                   |
|         |                   |     [2, 3, 4], [1, 2, 3, 4]]        |                   |
-----------------------------------------------------------------------------------------
| Ẋ       | Shuffle list      | 4Ẋ -> [1 ... 4]Ẋ -> [2, 1, 3, 4]    | Shuffles R        |
-----------------------------------------------------------------------------------------
| Œ!      | All permutations  | 3Œ! -> [1, 2, 3]Œ!                  | All permutations  |
|         | of a list         | -> [[1, 2, 3], [1, 3, 2],           | of R              |
|         |                   |     [2, 1, 3], [2, 3, 1],           |                   |
|         |                   |     [3, 1, 2], [3, 2, 1]]           |                   |
-----------------------------------------------------------------------------------------
| ŒḄ      | Non-vectorized    | 4ŒḄ -> [1 ... 4]ŒḄ                  | Bounces R         |
|         | bounce,           | -> [1, 2, 3, 4, 3, 2, 1]            |                   |
|         | z[:-1] + z[::-1]  |                                     |                   |
-----------------------------------------------------------------------------------------
| Œc      | Unordered pairs   | 4Œc -> [1 ... 4]Œc                  | Unordered pairs   |
|         | of a list         | -> [[1, 2], [1, 3], [1, 4], [2, 3], | of R              |
|         |                   |     [2, 4], [3, 4]]                 |                   |
-----------------------------------------------------------------------------------------
| Œċ      | Unordered pairs   | 4Œċ -> [1 ... 4]Œċ                  | Unordered pairs   |
|         | with replacement  | -> [[1, 1], [1, 2], [1, 3], [1, 4], | with replacement  |
|         | of a list         |     [2, 2], [2, 3], [2, 4], [3, 3], | of R              |
|         |                   |     [3, 4], [4, 4]]                 |                   |
-----------------------------------------------------------------------------------------
| ŒP      | Powerset of       | 3ŒP -> [1 ... 3]                    | Powerset of R     |
|         | a list            | -> ['', [1], [2], [3], [1, 2],      |                   |
|         |                   |     [1, 3], [2, 3], [1, 2, 3]]      |                   |
-----------------------------------------------------------------------------------------
| Œp      | Cartesian         | 4,2Œp -> [[1 ... 4], [1 ... 2]]Œp   | Cartesian product |
|         | product of z's    | -> [[1, 1], [1, 2], [2, 1], [2, 2], | of [1 ... z[i]]   |
|         | items             |     [3, 1], [3, 2], [4, 1], [4, 2]] | for i in z        |
-----------------------------------------------------------------------------------------

Sherlock9

Posted 2016-02-03T13:21:46.303

Reputation: 11 664

That's not entirely accurate. Yes, dequeue and Cartesian product do this, but sum wraps instead of creating a range, sort and reverse convert to base 10 first. – Dennis – 2016-12-31T07:45:52.357

2

It's sometimes worthwhile to read from standard input when there are exactly two inputs

Jelly is optimized for taking input from command-line arguments. However, it's also optimized for writing monads rather than dyads; with dyads there are so many possible meanings for each builtin that you often need to spend characters to disambiguate, whereas with monads there are typically many ways to say the same thing.

As such, if you use one of two inputs only once, and the problem is such that it can't easily be implicitly read from (i.e. you either need to make the explicit, or else spend a character on }, @, or the like), consider reading it from standard input with Ɠ rather than placing it on the command line; that lets you precisely place the input right where you need it via the placement of your Ɠ, whilst ensuring that every other implicit input will be taken from your other input. That costs a byte and saves a byte, and depending on the problem, may well save a second byte by giving you more scope to reorder the code.

user62131

Posted 2016-02-03T13:21:46.303

Reputation: