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\$Wezl– Wezl2022-02-01 23:17:31 +00:00Commented 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\$user– user2020-08-31 23:35:50 +00:00Commented 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
#definefor example, but I admit it's nice thatdefandvalare shorter. \$\endgroup\$Matthew Read– Matthew Read2011-11-11 18:21:17 +00:00Commented Nov 11, 2011 at 18:21 - \$\begingroup\$ Hm.
defis the keyword to define a method, and a simple translation to c++ forvalis '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\$user unknown– user unknown2011-11-11 19:01:00 +00:00Commented 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,typedefdoes work. Looking at thevalexample again I definitely misread it. It seems even less Scala-specific.x = thingWithAReallyLongComplicatedNameForNoReasonis a pretty general strategy :P \$\endgroup\$Matthew Read– Matthew Read2011-11-11 19:21:31 +00:00Commented Nov 11, 2011 at 19:21 - \$\begingroup\$ @userunknown When you instantiate a
ListorArrayetc with syntaxval x = List(1,2,3)you're just calling theapplymethod on theListobject. (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\$Luigi Plinge– Luigi Plinge2011-11-12 17:36:28 +00:00Commented 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 aStringlike aSeq, it's shorter). \$\endgroup\$user– user2021-01-22 21:12:52 +00:00Commented 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+.0instead of231.toDouble.231+""instead of231.toStringUse
Seqinstead ofArrayorListRewrite 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
defreturn type if possible. Usedefinstead of lambda functionval f=. e.g.use
def f(a:Int)=a+1instead 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-1instead 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\$noodle person– noodle person2023-11-02 00:38:39 +00:00Commented Nov 2, 2023 at 0:38 - \$\begingroup\$ @noodle man, so many thanks. \$\endgroup\$138 Aspen– 138 Aspen2023-11-02 00:39:21 +00:00Commented 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\$Jonathan Frech– Jonathan Frech2018-09-10 23:35:53 +00:00Commented Sep 10, 2018 at 23:35