The solution is already given but I think there is a need for more explanation:
You are only allowed to leave parentheses and the dot if your expression is in operator position. An expression is in operator position if it has the form <object> <method> <param>
. This is not the case with methods which contain multiple explicit parameter lists as foldLeft
. Thus you have to write <list>.foldLeft(<init>)(<function>)
. Nevertheless Scala has a special rule to work around this - you can insert another set of parentheses: (<list> foldLeft <init>) (<function>)
. Furthermore there is another method called /:
which is a synonym to foldLeft
, defined as def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)
. It allows you to write (<init> /: <list>) (<function>)
. Maybe you just noticed here that the symbols between the first parentheses are swapped - this is because of the rule that each method ending with a colon is right- instead of left-associative (further explanation).
Now I want to give you some hints for further refactorings:
Tuple2[A, B]
can be written as (A, B)
- You don't have to write all the types. Some of them can - and should - be left to clear up your code (I know you are a beginner and want to write this. Only as a hint...). But don't leave
- Lists are mostly named something like
xs
or ys
because this means "lots of x" or "lots of y". This is not very important but common
- You can patter match on parameters to extract them to easy to read names:
... { case (a, (b,c)) => ...}
- Your code does not work as it claims the task. You need something like
List.fill(<n>)(<elem>)
- Don't append elements to a list, this is
O(n)
. :::
implicitly is a append operation - look at the sources.
- For this task
foldLeft
is not be best solution to come up with. foldRight
or the synonym :\
may be more efficient because the :::
operation requires less elements to copy. But I prefer flatMap
(see below), which is a map+flatten
- You can use a for-comprehension to solve this, which often will be easy to read. See this for more information how for-comprehensions are realized internally.
All in all the example solutions:
object Test extends App {
def decode1(l: List[Tuple2[Int, Symbol]]): List[Symbol] =
l.foldLeft(List[Symbol]()) { (symbols: List[Symbol], e: Tuple2[Int, Symbol]) => symbols ::: List.fill(e._1)(e._2) }
def decode2(xs: List[(Int, Symbol)]): List[Symbol] =
(xs foldLeft List.empty[Symbol]) { case (xs, (n, s)) => xs ::: List.fill(n)(s) }
def decode3(xs: List[(Int, Symbol)]): List[Symbol] =
(xs foldRight List.empty[Symbol]) { case ((n, s), xs) => List.fill(n)(s) ::: xs }
def decode4(xs: List[(Int, Symbol)]): List[Symbol] =
(List.empty[Symbol] /: xs) { case (xs, (n, s)) => xs ::: List.fill(n)(s) }
def decode5(xs: List[(Int, Symbol)]): List[Symbol] =
xs flatMap { case (n, s) => List.fill(n)(s) }
def decode6(xs: List[(Int, Symbol)]): List[Symbol] =
for {
(n, s) <- xs
ys <- List.fill(n)(s)
} yield ys
val xs = List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e))
val ys = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
println("start testing")
val tests = List[List[(Int, Symbol)] => List[Symbol]](decode1, decode2, decode3, decode4, decode5, decode6)
for (t <- tests)
assert(t(xs) == ys)
println("finished")
}