What general tips do you have for golfing in Scala? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Scala (e.g. "remove comments" is not an answer). Please post one tip per answer.
16 Answers
The shortest way of repeating something is with Seq.fill
.
1 to 10 map(_=>println("hi!")) // Wrong!
for(i<-1 to 10)println("hi!") // Wrong!
Seq.fill(10)(println("hi!")) // Right!
-
\$\begingroup\$
for(i<-1to 10)println("hi!")
has the same length \$\endgroup\$– WezlCommented Feb 1, 2022 at 23:17
suspicious identifier: ?
You can use ? as identifier:
val l=List(1,2,3)
val? =List(1,2,3)
Here it doesn't save you anything, because you can't stick it to the equal sign:
val ?=List(1,2,3) // illegal
But later on, it often saves one character, since you don't need a delimiter:
print(?size) // l.size needs a dot
def a(? :Int*)=(?,?tail).zipped.map(_-_)
However, it is often tricky to use:
print(?size)
3
print(?size-5)
<console>:12: error: Int does not take parameters
print(?size-5)
^
-
2\$\begingroup\$ There are also other identifiers, such as
|
,>
, and pretty much any symbol that's not a word character (other than parentheses, commas, dots, semicolons, equal signs, square brackets, curly brackets...) \$\endgroup\$– userCommented Aug 31, 2020 at 23:35
Collections
The first choice for a random collection is often List. In many cases you can replace it with Seq, which saves one character instantan. :)
Instead of
val l=List(1,2,3)
val s=Seq(1,2,3)
and, while s.head and s.tail is more elegant in usual code, s(0)
is again one character shorter than s.head
.
Even shorter in some cases - depending on needed functionality is a tuple:
val s=Seq(1,2,3)
val t=(1,2,3)
saving 3 characters immediately, and for accessing:
s(0)
t._1
it is the same for direct index access. But for elaborated concepts, tuples fail:
scala> s.map(_*2)
res55: Seq[Int] = List(2, 4, 6)
scala> t.map(_*2)
<console>:9: error: value map is not a member of (Int, Int, Int)
t.map(_*2)
^
update
def foo(s:Seq[Int])
def foo(s:Int*)
In parameter declaration, Int* saves 4 characters over Seq[Int]. It is not equivalent, but sometimes, Int* will do.
disclaimer: parts of this answers are generalizations of other answers found here.
Use lambdas without specifying their argument types
It's allowed to submit something like this: a=>a.size
instead of (a:String)=>a.size
.
Use ascii-symbols as identifiers.
These include !%&/?+*~'-^<>|
. Because they arent't letters, they get parsed separately when they're next to letters.
Examples:
a=>b //ok
%=>% //error, parsed as one token
% => % //ok
val% =3 //ok
&contains+ //ok
if(x)&else* //ok
Use Set instead of contains
if (Seq(1,2,3,'A')contains x)... //wrong
if (Set(1,2,3,'A')(x))... //right
This is possible because Set[A] extends (A => Boolean)
.
Use a curried function when you need two arguments.
(a,b)=>... //wrong
a=>b=>... //right
Use the _
-syntax when possible
The rules for this are somewhat obscure, you have to play a little bit around sometimes to find the shortest way.
a=>a.map(b=>b.size)) //wrong
a=>a.map(_.size) //better
_.map(_.size) //right
Use partial application
a=>a+1 //wrong
_+1 //better, see above
1+ //right; this treats the method + of 1 as a function
Use ""+
instead of toString
a=>a.toString //wrong
a=>a+"" //right
Use strings as sequences
""
is sometimes the shortest way to create an empty sequence if you don't care about the actual type
Use BigInt to convert numbers to and from strings
The shortest way to convert a number to a string in a base other than base 10 is through BigInt's toString(base: Int)
method
Integer.toString(n,b) //wrong
BigInt(n)toString b //right
If you want to convert a string to a number, use BigInt.apply(s: String, base: Int)
Integer.parseInt(n,b) //wrong
BigInt(n,b) //right
Be aware that this returns a BigInt, which is useable like a number most of the times, but can't be used as an index for a sequence, for example.
Use Seq to create sequences
a::b::Nil //wrong
List(...) //also wrong
Vector(...) //even more wrong
Seq(...) //right
Array(...) //also wrong, except if you need a mutable sequence
Use Strings for Seqences of chars:
Seq('a','z') //wrong
"az" //right
Make use of Stream for infinite sequences
Some challenges ask for the n-th element of an infinite sequence. Stream is the perfect candidate for this. Remember that Stream[A] extends (Int => A)
, that is, a stream is a function from an index to the element at that index.
Stream.iterate(start)(x=>calculateNextElement(x))
Use symbolic operators instead of their wordy counterparts
:\
and :/
instead of foldRight
and foldLeft
a.foldLeft(z)(f) //wrong
(z/:a)(f) //right
a.foldRight(z)(f) //wrong
(a:\z)(f) //right
hashCode
-> ##
throw new Error()
-> ???
Use ->
for creating and unpacking tuples
(a,b) //wrong
a->b //right
Use &
and |
instead of &&
and ||
They work the same for booleans, but will always evaluate both operands
Alias long method as functions
def r(x:Double)=math.sqrt(x) //wrong
var r=math.sqrt _ //right; r is of type (Double=>Double)
Know the functions in the standard library
This especially applies to the methods of collections.
Very useful methods are:
map
flatMap
filter
:/ and :\ (folds)
scanLeft and scanRight
sliding
grouped (only for iterators)
inits
headOption
drop and take
collect
find
zip
zipWithIndex3
distinct and/or toSet
startsWith
Use infix syntax to remove the need for .
characters. You don't need spaces unless adjacent items are both in alphanumeric or both in operator characters (see here), and not separated by reserved characters (brackets, commas etc).
E.g.
List(1,2,3,4).filter(_ % 2 == 0) // change to:
List(1,2,3,4)filter(_%2==0)
You can usually use map
instead of foreach
:
List("a","b","c") foreach println
can be replaced with
List("a","b","c") map println
The only difference is the return type (Unit
vs List[Unit]
), which you aren't interested in anyway when using foreach
.
The true
and false
literals are shorter to write as 2>1
for true and 1>2
for false
Call two times the same function for initialization:
val n,k=readInt
(Seen somewhere else, but can't find it now).
define shorter Types:
If you have multiple declarations of a type, like
def f(a:String,b:String,c:String)
it is shorter to define a type alias, and use it instead:
type S=String;def f(a:S,b:S,c:S)
Original length is 3*6=18 Replacement-code is 8(type S=;)+6+3*1(=new length)=17
if (n*length < 8+length+n), then it is an advantage.
For classes which are instantiated via a factory, we can set a shorter variable name to point to that object. Instead of:
val a=Array(Array(1,2),Array(3,4))
we can write
val A=Array;val a=A(A(1,2),A(3,4))
-
1\$\begingroup\$ This applies to C++ as well with
#define
for example, but I admit it's nice thatdef
andval
are shorter. \$\endgroup\$ Commented Nov 11, 2011 at 18:21 -
\$\begingroup\$ Hm.
def
is the keyword to define a method, and a simple translation to c++ forval
is 'const', and it is a declaration, but the type is often inferred. The shortening is in the first case thetype=
which is closer totypedef
- isn't it? The second example isn't from me and it is new to me. I have to watch out, where to use it. \$\endgroup\$ Commented Nov 11, 2011 at 19:01 -
\$\begingroup\$
typedef long long ll;
is the same as#define ll long long
, so the latter's shorter by 1. But yeah,typedef
does work. Looking at theval
example again I definitely misread it. It seems even less Scala-specific.x = thingWithAReallyLongComplicatedNameForNoReason
is a pretty general strategy :P \$\endgroup\$ Commented Nov 11, 2011 at 19:21 -
\$\begingroup\$ @userunknown When you instantiate a
List
orArray
etc with syntaxval x = List(1,2,3)
you're just calling theapply
method on theList
object. (This technique for object creation is known as a "factory method", in contrast to using a constructor withnew
.) So above, we're just making a new variable that points to the same singleton object as the variable nameArray
. Since it's the same thing, all the methods, includingapply
, are available. \$\endgroup\$ Commented Nov 12, 2011 at 17:36
Rename Methods, if their name is long, and if they're used multiple times - real world example:
x.replaceAll(y,z)
type S=String; def r(x:S,y:S,z:S)=x.replaceAll(y,z)
Depending on the possibility to save 'S=String' at different places too, this will only be economical, if you replace at least replaceAll 3 times.
Initialize several variables at once using a tuple:
var(a,b,c)=("One","Two","Three") //32 characters
vs.
var a="One";var b="Two";var c="Three" //37 characters
Use lazyZip
or zipped
(and _
) instead of zip
when combining lists
Using a.zip(b)
is shorter than a.lazyZip(b)
or (a,b).zipped
, but if you're using map
on it later, the latter will be shorter, since with zip
, map
requires function taking a single Tuple2
, but with lazyZip
/zipped
, you can use a function of two parameters with underscores.
Here's an example adding together two lists:
val list1 = List(1, 2, 3, 4)
val list2 = List(5, 6, 7, 8)
list1.zip(list2).map(t=>t._1+t._2) //Not good
list1.lazyZip(list2).map((a,b)=>a+b) //Okay, this is worse
list1.lazyZip(list2).map(_+_) //But underscores make it all better
(list1,list2).zipped.map((a,b)=>a+b) //Same as with lazyZip
(list1,list2).zipped.map(_+_) //Same as with lazyZip
Since zipped
is deprecated, I'd recommend lazyZip
, as you can also use infix syntax with it. However, in some situations, such as when you're zipping more than 2 lists, zipped
may still be better (and it's not available on TIO, which uses an older version of Scala - you'll need Scastie).
Treat a String as a Sequence
Use filter
instead of replace
or replaceAll
If you need to erase characters from a String use instead of replace
/ replaceAll
x replaceAll(y,"") // 18 bytes
x replace(y,"") // 15 bytes
the filter
method:
x filter(y!=) // 13 bytes
Do not use charAt
If you need a character from a specific index from a String, do not use charAt
:
x charAt 7 // 10 bytes
Treat the String as a sequence:
x(7) // 4 bytes
Use drop
/ slice
instead of substring
x substring y // 13 bytes
x drop y // 8 bytes
x substring(a,b) // 16 bytes
x slice(a,b) // 12 bytes
Thanks to user for extending the answer!
-
1\$\begingroup\$ If you want to expand your answer(s) with more String-related tips, here's another:
s.drop(x)
is shorter thans.substring(x)
, ands.slice(a,b)
is shorter thans.substring(a,b)
(really, almost any time you treat aString
like aSeq
, it's shorter). \$\endgroup\$– userCommented Jan 22, 2021 at 21:12
- Use syntactic sugar and built-in functions to save bytes.
syntactic sugar, e.g. use /: operator (fold left)
val sum = numbers.foldLeft(0) { (accumulator, element) => accumulator + element }
val sum = (0 /: numbers) { (accumulator, element) => accumulator + element }
Type conversion.
231+.0
instead of231.toDouble
.231+""
instead of231.toString
Use
Seq
instead ofArray
orList
Rewrite type name and method name.
Rewrite the type name if the type appears very many times.
Use generic parameter instead of external type definition to save more bytes.
/*42 bytes*/
def f(a:String,b:String,c:String,d:String)
/*36 bytes*/
type S=String
def f(a:S,b:S,c:S,d:S)
/*33 bytes*/
def f[S<:String](a:S,b:S,c:S,d:S)
If the method appears very many times, please rewrite the method name.
implicit class V[A](val x:A)extends AnyVal{def Q=x.toSet}
Avoid
def
return type if possible. Usedef
instead of lambda functionval f=
. e.g.use
def f(a:Int)=a+1
instead ofval f:(Int=>Int)={a:Int=>a+1}
to save bytes.Avoid unnecessary braces
{} ()
Simultaneously initialize multiple variables
val r,c=new StringBuilder;
use
0 to x-1
instead of0 until x
-
\$\begingroup\$ Use generic parameter instead of external type definition:
def f[S<:String](a:S,b:S,c:S,d:S)
This is shorter than just inlining the type if you have more than 2 arguments of that type. \$\endgroup\$ Commented Nov 2, 2023 at 0:38 -
\$\begingroup\$ @noodle man, so many thanks. \$\endgroup\$ Commented Nov 2, 2023 at 0:39
If you need to reuse a function, make a def
instead of a val
If you absolutely need to define a function in your answer to reuse multiple times, it's usually better to define it like a method rather than using a lambda.
Consider these two pairs of functions:
//Recursive
def f(i:Int):Int=if(i>0)i*f(i-1)else 1
val f:Int=>Int=i=>if(i>0)i*f(i-1)else 1
//Not recursive is also shorter with def
def f(i:Int)=i+1
val g=(i:Int)=>i+1
Except when using pattern matching
However, if you're pattern matching, you can use Scala's special syntax and omit match
when assigning an anonymous function to a val
. As you can see, it's a lot shorter:
//Highly efficient way to sum a list
val g:List[Int]=>Int={case h::t=>h+f(t)case _=>0}
def f(l:List[Int]):Int=l match{case h::t=>h+f(t)case _=>0}
You can also use ⇒
instead of using =>
for function definitions.
-
5\$\begingroup\$ Hello and welcome to PPCG. Since most of the time, answers are counted in bytes rather than characters, your tip only has a limited scope. I would address this and also add a tip title like Shortening function definitions in character count based code golf challenges. \$\endgroup\$ Commented Sep 10, 2018 at 23:35