15
\$\begingroup\$

What general tips do you have for golfing in Racket / Scheme? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Racket / Scheme (e.g. "remove comments" is not an answer).


I'm aware Scheme and Racket (formerly PLT Scheme) are technically different languages but are quite similar in many ways and much code (I suspect) will run mostly as intended in either. If your tip only applies to one of the aforementioned languages, note as such.

\$\endgroup\$
3
\$\begingroup\$

The expressions 'x, `x, ,x, an ,@x automatically expand to (quote x), (quasiquote x), (unquote x), and (unquote-splicing x), respectively. This is purely a syntactic transformation, and can be applied anywhere. This gives a convenient notation for one-variable functions:

; Defining a function:
(define ,x (+ x x))
; Calling a function:
(display ,2)

which expands to

; Defining a function:
(define (unquote x) (+ x x))
; Calling a function:
(display (unquote 2))

I'm not sure what the semantics for shadowing a syntactic keyword such as quote or quasiquote with a bound variable, although code like the above worked in the interpreters I tested it on, and unquote-splicing is less than ideal since it has a two-character abbreviation, but unquote is an auxillary syntax with a one-character abbreviation and so is ideal for this hack.

\$\endgroup\$
8
\$\begingroup\$

In Racket, λ and lambda are synonymous keywords for constructing anonymous functions, but λ is 2 bytes where lambda is 6.

In Scheme, there's no such keyword λ and you're stuck with lambda.

\$\endgroup\$
6
\$\begingroup\$

Use ~a to convert numbers and symbols to strings.

\$\endgroup\$
5
\$\begingroup\$

When using Racket, bind variables using λ to shave off a few bytes. In Scheme, lambda makes this trick not applicable, unless one is binding four or more variables.

Example: One variable saves 2 bytes over let/define

(define n 55)(* n n) ; 20 bytes

(let([n 55])(* n n)) ; 20 bytes

((λ(n)(* n n))55) ; 18 bytes
\$\endgroup\$
  • \$\begingroup\$ I wouldn't call that binding. You are using different functions. In some cases, using an anonymous function is shorter than binding a variable. \$\endgroup\$ – Michael Vehrs Mar 17 '17 at 6:52
  • \$\begingroup\$ I'm not sure what your opinion has to do with the typical terminology used in scheme circles. I can assure you both ways are binding variables to a lexical scope, and let is often implemented in terms of lambda. \$\endgroup\$ – Winny Mar 18 '17 at 23:08
5
\$\begingroup\$

In Racket, require forms can have multiple arguments.

(require net/url net/uri-codec)

Is way shorter than

(require net/url)(require net/uri-codec)

I don't know much about Scheme, but it doesn't seem to have a require builtin.

\$\endgroup\$
5
\$\begingroup\$

Use shorter synonyms

There's a number of procedures in Racket that have mostly-equivalent shorter versions. (They are usually not equivalent: for example, (car (cons 1 2)) works where (first (cons 1 2)) fails. But you can make the substitution if you know they are synonyms in your case.)

This list is probably incomplete: I probably don't know about most of the things that could go in this list yet.

  • (= a b) instead of (equal? a b) when comparing numbers.
  • '(1 2) instead of (list 1 2).
  • car, cadr, cdr for first, second, and rest.
  • null? instead of empty?
  • modulo instead of remainder when the modulus is positive.
  • floor instead of truncate when its argument is positive.
\$\endgroup\$
4
\$\begingroup\$

Omit needless spaces

This can be considered a "trivial" tip, but it has to be pointed out somewhere.

Any time you read Racket code written by normal people (e.g. in the Racket documentation) it will have all the spaces put in: for example,

(append (list 1 2) (list 3 4) (list 5 6) (list 7 8))

In fact, since ( and ) can't be part of variable names, we can delete all the spaces around them and not lose any ambiguity (and, more importantly, still get valid code). So the above expression can instead be:

(append(list 1 2)(list 3 4)(list 5 6)(list 7 8))
\$\endgroup\$
2
\$\begingroup\$

The following tips are for Racket:

Default arguments

Especially useful for creating aliases for long function names that are used often.

Assume the golf allows you to write a function that consumes the argument, and assume you need to use reverse a lot. You'll start with something like:

(λ(x) ... reverse ... reverse ... reverse ...

You can instead take in an additional argument, with a shorter name than reverse, and set its default value to reverse:

(λ(x[r reverse]) ... r ... r ... r ...

Furthermore, it's useful if you have a helper function that you use in many places with some of the same arguments. Remember to reorder the arguments to the function as needed, so you can use as many default arguments as possible, and remove the arguments from multiple callsites.

match

This one is a little harder to summarize in a small post, so read up on the Racket Docs for this one. In a nutshell, match allows you extract elements and sequences of elements in a certain order from a list, and the quasiquote syntax lets you stitch the mutilated list back together:

(match (range 10)
 [`(,xs ... 3 ,ys ... 6 ,zs ...)
  `(,@(map f xs) 3 ,@(map f ys) 6 ,@(map f sz))]
 ...

It also gives you an easy way to work with regular expressions and do additional computation on the resulting groups afterwards,

Named let

See the named let proc-id ... syntax here.

This allows you to write recursive functions that are called immediately without define or actually calling the function after you've defined it.

Something like:

(define (fib i)
  (if (< i 2) i
      (+ (fib (- i 1)) (fib (- i 2)))))
(fib 10)

can be shortened to:

(let fib {[i 10]}
  (if (< i 2) i
      (+ (fib (- i 1)) (fib (- i 2)))))


This last one is silly, but I haven't been able to use this little trick anywhere so far:
(apply map list matrix) takes a transpose of matrix, where matrix is some rectangular list of lists, like '((1 2 3) (a b c)).
Let me know if this does prove to be useful.

\$\endgroup\$
1
\$\begingroup\$

As Winny pointed out, #! can usually be used instead of #lang, saving four bytes.

#lang racket ;12 bytes
#!racket ;8 bytes
\$\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.