Tips for golfing in Knight

Knight is the Language of the Month for August 2022, and I noticed that it didn't have a page, so here it is!

I'm looking for ideas which can be applied to problems and which are also at least somewhat specific to Knight (e.g. "remove comments" is not an answer).

Variables in Knight consist of a lowercase letter followed by additional lowercase letters or numbers, but not uppercase letters. This means that you don't need a space between a variable and a function call -- e.g.

Oa Ob
OaOb


But you can't do

O+a1


because the 1 will be parsed as part of the variable name. To get around this, swap the argument order:

O+1a


Use coercion

For example, in the case that + receives a string as the first and a number as the second arg, it will coerce the second one to a string and concatenate them. However, if you do it vice versa, with a number as the first and a string as the second, it will convert the second to a number and add the two. This means that you can use +0 to convert a string to a number, and +"" to convert a number to a string.

However, sometimes, +0 can be one byte shorter! E, the evaluate function, will evaluate its argument as Knight code. So given a number-string, it will convert to a number. Often, however, this will cost 1 or 2 bytes due to whitespace being needed.

With *, if the first argument is a string and the second a number, it will repeat the string n times instead of multiplying. In the case of num+str, it will coerce the string to a number. And with str+str, it will coerce the second to a number and repeat the first string that many times.

However, with -, /, ^, and %, if the first is a number, the second will be converted to a number. But if the first is a string, it's undefined behavior.

Some other operators like < and > will also coerce - see the spec.

• I think almost every function does type coercion except for ? (comparison). Aug 7 at 22:49

Using & and | instead of IF

Unless you need both the if true and if false clauses of IF, you can simply use & or |.

Instead of Ifoo bar <whatever>, you can do &foo bar. Likewise, instead of Ifoo <whatever> bar (or I!foo bar <whatever>), you can do |foo bar.

The ASCII function in Knight, if given a string, returns the ASCII value of just the first character of a string; if given a number, it returns the ASCII character with that code point. This means that ASCII (ASCII s) will return the first character of string s. You can use this along with GET to iterate through a string:

;=pP;=i~1W<=i+1iLpO Gp i 1
;=pP Wp;O A Ap=pGp 1Lp


Here's the code snippets written out in easier to understand format, with basically equivalent Python code on the side:

;= p PROMPT                         # p = input()
;= i ~1                             # i = -1
WHILE (< (= i (+1 i)) (LENGTH p)   # while (i = i+1) < len(p):
OUTPUT (GET p i 1)               #   print(p[i:i+1])

;= p PROMPT                         # p = input()
WHILE p                            # while p != "":
;OUTPUT (ASCII (ASCII p))          #   print(p[0])
= p (GET p 1 (LENGTH p))          #   p = p[1:len(p)+1]


Technically, the call to Gp 1Lp is undefined since + 1 LENGTH p is outside of the bounds of the string p. In order to make it fully spec-compliant, it would have to be

;=pP Wp;O A Ap=pGp 1-Lp 1


However, the first version works within the C TIO.

Edit: Using @Sampersand's suggestions, you could alternatively do

;=pP Wp;O GpF1=pSpF1""


Which is the same number of characters but spec-compliant. In this case, we can additionally use the fact that OUTPUT returns a falsy value to shorten it to

;=pP Wp=pSpO GpF1T""


Which saves two bytes.

• It also works in the JS interpreter so it should be good Aug 7 at 19:16
• To get the first character, you can do Gsome_stringF1—get the substring starting at F (ie 0) of length 1. To get the rest of the characters, you can do Ssome_stringF1""—return a new string with the range F..F+1 (ie 0..1) replaced with an emoty string Aug 14 at 21:45

Since the assignment operator in Knight returns the assigned value, you can chain together definitions if, for example, you want to create multiple variables with the same value:

;=a 0;=b 0=c 0
=a=b=c 0

• You should edit the LOTM page with links to your tips in the "List of relevant tips posted in August 2022" section. Aug 7 at 6:46

At the risk of stating the obvious, all functions can be abbreviated to a single uppercase character. For example:

OUTPUT 1
O 1


You also can remove the space between the function and the parameter:

O1


But if you want to nest function calls, you need to put a space between them:

OP   # Parsed as a single function "OP", which is equivalent to "OUTPUT"
O P  # Parsed as "OUTPUT PROMPT"


Use T or F instead of 1 or 0 to save a character

Examples:

Save a byte when initializing to 0

Instead of ;=n 0 you can do ;=nF, but this will require you to ensure that you're coercing a later on. But, if you're using n as a counter, you can simply do W>100=n+1n, which'll coerce the initial F to 0.

Getting the first character of a string

Instead of Gs 0 1, you can do GsF1—since all arguments to G are coerced to the appropriate types, this'll always work.

Getting the rest of a string

Instead of Ss 0 1 "" (set the range 0..0+1 to an empty string), you can do SsF1"". This works in a similar fashion to G

• This can also save a byte or two when doing arithmetic. For example, -n 1 (where n is an integer) can be shortened to -nT. Aug 15 at 17:15