I have a sequence like this:
val l = Seq(1,2,3,4)
which I want to transform to List(Seq(1,2), Seq(2,3), Seq(3,4))
Here is what I tried:
def getPairs(inter: Seq[(Int, Int)]): Seq[(Int, Int)] = l match {
case Nil => inter
case x :: xs => getPairs(inter :+ (x, xs.head))
}
This strangely seems not to work? Any suggestions?
You can also just use sliding:
l.sliding(2).toList
res1: List[Seq[Int]] = List(List(1, 2), List(2, 3), List(3, 4))
Ok I got to know about the zip method:
xs zip xs.tail
Using a for comprehension, for instance as follows,
for ( (a,b) <- l zip l.drop(1) ) yield Seq(a,b)
Note l.drop(1) (in contrast to l.tail) will deliver an empty list if l is empty or has at most one item.
The already given answers describe well, how to do this in a scala way.
However, you might also want an explanation why your code does not work, so here it comes:
Your getPairs function expects a list of tuples as input and returns a list of tuples. But you say you want to transform a list of single values into a list to tuples. So if you call getPairs(l) you will get a type mismatch compiler error.
You would have to refactor your code to take a simple list:
def pairs(in: Seq[Int]): Seq[(Int, Int)] = {
#tailrec
def recursive(remaining: Seq[Int], result: Seq[(Int, Int)]): Seq[(Int, Int)] = {
remaining match {
case Nil => result
case last +: Nil => result
case head +: next +: tail => recursive(next +: tail, (head, next) +: result)
}
}
recursive(in, Nil).reverse
}
and from here it's a small step to a generic function:
def pairs2[A](in: Seq[A]): Seq[(A, A)] = {
#tailrec
def recursive(remaining: Seq[A], result: Seq[(A, A)]): Seq[(A, A)] = {
remaining match {
case Nil => result
case last +: Nil => result
case head +: next +: tail => recursive(next +: tail, (head, next) +: result)
}
}
recursive(in, Nil).reverse
}
Related
Suppose I've got case class A(x: Int, s: String) and need to update a List[A] using a Map[Int, String] like that:
def update(as: List[A], map: Map[Int, String]): List[A] = ???
val as = List(A(1, "a"), A(2, "b"), A(3, "c"), A(4, "d"))
val map = Map(2 -> "b1", 4 -> "d1", 5 -> "e", 6 -> "f")
update(as, map) // List(A(1, "a"), A(2, "b1"), A(3, "c"), A(4, "d1"))
I am writing update like that:
def update(as: List[A], map: Map[Int, String]): List[A] = {
#annotation.tailrec
def loop(acc: List[A], rest: List[A], map: Map[Int, String]): List[A] = rest match {
case Nil => acc
case as => as.span(a => !map.contains(a.x)) match {
case (xs, Nil) => xs ++ acc
case (xs, y :: ys) => loop((y.copy(s = map(y.x)) +: xs) ++ acc, ys, map - y.x)
}
}
loop(Nil, as, map).reverse
}
This function works fine but it's suboptimal because it continues iterating over the input list when map is empty. Besides, it looks overcomplicated. How would you suggest improve this update function ?
If you can not make any supposition about the List and the Map. Then the best is to just iterate the former, juts once and in the simplest way possible; that is, using the map function.
list.map { a =>
map
.get(key = a.x)
.fold(ifEmpty = a) { s =>
a.copy(s = s)
}
}
However, if and only if, you can be sure that most of the time:
The List will be big.
The Map will be small.
The keys in the Map are a subset of the values in the List.
And all operations will be closer to the head of the List rather than the tail.
Then, you could use the following approach which should be more efficient in such cases.
def optimizedUpdate(data: List[A], updates: Map[Int, String]): List[A] = {
#annotation.tailrec
def loop(remaining: List[A], map: Map[Int, String], acc: List[A]): List[A] =
if (map.isEmpty) acc reverse_::: remaining
else remaining match {
case a :: as =>
map.get(key = a.x) match {
case None =>
loop(
remaining = as,
map,
a :: acc
)
case Some(s) =>
loop(
remaining = as,
map = map - a.x,
a.copy(s = s) :: acc
)
}
case Nil =>
acc.reverse
}
loop(remaining = data, map = updates, acc = List.empty)
}
However note that the code is not only longer and more difficult to understand.
It is actually more inefficient than the map solution (if the conditions are not meet); this is because the stdlib implementation "cheats" and constructs the List my mutating its tail instead of building it backwards and then reversing it as we did.
In any case, as with any things performance, the only real answer is to benchmark.
But, I would go with the map solution just for clarity or with a mutable approach if you really need speed.
You can see the code running here.
How about
def update(as: List[A], map: Map[Int, String]): List[A] =
as.foldLeft(List.empty[A]) { (agg, elem) =>
val newA = map
.get(elem.x)
.map(a => elem.copy(s = a))
.getOrElse(elem)
newA :: agg
}.reverse
I would like to understand why this code is not compiling:
def comb(list: List[(Char, Int)]): List[(Char, Int)] = {
for (tuple <- list) {
tuple match {
case p if (p._2 > 1) => List(p) :: List((p._1, p._2 - 1))
case _ => List(tuple)
}
}
}
If I execute this code outside the function and applied directly to a List it works. I think my problem is, I don't understand how values are returned.
I got this error from Eclipse:
type mismatch; found : Unit required: List[(Char, Int)]
I reviewed another answers, mostly with if statements, but I still don't get it.
A for() without a yield is equivalent to foreach(), which returns nothing of interest (a Unit), but the definition of comb() says that it should return List[(Char, Int)], so the compiler complains.
We can add a yield after the for() but there's another problem: the code is creating List values inside the for(), which means that the result will be a List of many smaller Lists. That still doesn't match the method return type.
So we can .flatten the internal Lists to get just one List of tuples and this will finally match the return type.
def comb(list: List[(Char, Int)]): List[(Char, Int)] = {
for (tuple <- list) yield {
tuple match {
case p if p._2 > 1 => p :: List((p._1, p._2 - 1))
case _ => List(tuple)
}
}
}.flatten
comb(List(('a',9),('b',5),('c',1)))
//res0: List[(Char, Int)] = List((a,9), (a,8), (b,5), (b,4), (c,1))
It's worth noting that a for() comprehension with only one generator (the <- part) is equivalent to a map() call. And a map() call followed by .flatten is equivalent to a flatMap() call. So the above is equivalent to:
def comb(list: List[(Char, Int)]): List[(Char, Int)] =
list.flatMap{case (c,i) => (c,i) :: (if (i>1) (c,i-1)::Nil else Nil)}
You will need a yield over there
def comb(list: List[(Char, Int)]): List[(Char, Int)] = {
for (tuple <- list) yield {
tuple match {
case p if (p._2 > 1) => p :: List((p._1, p._2 - 1))
case _ => List(tuple)
}
}
}
However this is possibly not the best way to do it in Scala. You could try something like this as an alternative
def comb(list: List[(Char, Int)]): List[(Char, Int)] = {
list.map{
case(char:Char,int:Int) if(int >1)=> (char,int-1)
case(char:Char,int:Int) => (char,int)
}
}
I'm trying to recursively iterate through a list in Scala using pattern matching. I cannot use any list functions, or while/for loops. What I need to do is iterate through the list, and remove an element if it matches to be '4'. I'm new to Scala and I cannot find the answer in the textbook I have nor on google. Everyone else uses the filter method, or some other list method.
Here's what I tried to do (which is wrong)
def removeFours(lst: List[Int]): List[Int] = {
val newLst = lst
lst match {
case Nil => Nil
case a if a == 4 => newLst -= 0
case n => removeFours(newLst)
}
newLst
}
See if this works for you.
def removeFours(lst: List[Int], acc: List[Int] = List.empty): List[Int] = {
lst match {
case Nil => acc.reverse
case 4 :: t => removeFours( t, acc )
case h :: t => removeFours( t, h :: acc )
}
}
Usage:
scala> removeFours( List(3,7,4,9,2,4,1) )
res84: List[Int] = List(3, 7, 9, 2, 1)
Using an inner function and pattern matching to de-structure the list. If the head in the list is 4, then do not add it to the accumulator. If it is, append it to the accumulator.
def removeFours(lst: List[Int]): List[Int] = {
def loop(lst: List[Int], acc: List[Int]): List[Int] = lst match {
case Nil => acc
case h :: t =>
if (h == 4) {
loop(t, acc)
}else{
loop(t, acc :+ h)
}
}
loop(lst, List())
}
The preferred way to do this is with guards in the pattern match but the if else statement may look more familiar if you're just getting started with scala.
def removeFours(lst: List[Int]): List[Int] = {
def loop(lst: List[Int], acc: List[Int]): List[Int] = lst match {
case Nil => acc
case h :: t if (h == 4) => loop(t, acc)
case h :: t => loop(t, acc :+ h)
}
loop(lst, List())
}
I am not sure about the execution time. I am also new to scala but I am taking bollean approach to filter any list.
object Main extends App {
//fun that will remove 4
def rm_4(lst: List[Int]) : List[Int] = {
val a = lst.filter(kill_4)
a
}
// boolean fun for conditions
def kill_4(n: Int) : Boolean = {
if (n ==4) false
else true
}
println(rm_4(List(1,2,4,5,4))) // outpur List(1,2,5)
}
When pattern matching a list, it seems common to return an empty list when given an empty list. We can match an empty list to Nil or List(), but we can return empty as Nil, List() or by returning the given list argument itself.
What's the convention here?
When would you choose one method over another?
Examples:
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case List() => Nil
case x :: xs => ???
}
def givenEmptyNumsReturnsEmptyList(nums: List[Int]): List[Int] = nums match {
case List() => List()
case x :: xs => ???
}
def givenEmptyNumsReturnsNums(nums: List[Int]): List[Int] = nums match {
case List() => nums
case x :: xs => ???
}
I'm scala beginner and don't know any existing convention about it. My things about it:
The last one isn't intuitive
I prefer return that I match. If I have case List(), so I return List().
You can also match Nil:
-
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case Nil => Nil
case x :: xs => ???
}
But Nil and List() are the same.
For choice better way, just clarify what inside:
case N1:
def givenEmptyNumsReturnsEmptyList(nums: List[Int]): List[Int] = nums match {
case List() => List()
case x :: xs => ???
}
Will call unaplay method from object List, after will call apply method of object List.
case N2:
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case Nil => Nil
case x :: xs => ???
}
Will compare value before match with object Nil and will return object Nil
And in case of choice I prefer case N2 because it is little bit optimal.
The code below is based on Merge sort from "Programming Scala" causes stack overflow
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {
def merge(xs: List[T], ys: List[T], acc: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys.reverse ::: acc
case (_, Nil) => xs.reverse ::: acc
case (x :: xs1, y :: ys1) =>
if (less(x, y)) merge(xs1, ys, x :: acc)
else merge(xs, ys1, y :: acc)
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(less)(ys), msort(less)(zs), Nil).reverse
}
}
When I try to invoke msort using :
val l = List(5, 2, 4, 6, 1, 3)
msort[Int](l)
I receive error :
Multiple markers at this line - type mismatch; found : List[Int] required: (Int, Int) => Boolean - type mismatch;
found : List[Int] required: (Int, Int) => Boolean - missing arguments for method msort in object mergesort; follow
this method with `_' if you want to treat it as a partially applied function
How do I invoke msort & why is a function required as part of the invocation ?
In Scala it is possible to have Multiple Parameters Lists. Your invocation only passes one argument.
The method is declared as def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T], so the first argument is of type (T, T) => Boolean, which is a function taking two parameters of type T and returning a Boolean value. You pass there a List[Int], which makes Scala complain.
Why would you like to have such a thing you may ask. Well, consider following example.
val stringSorter = msort[String]((a, b) => a.compareTo(b) < 0) _
// stringSorter: List[String] => List[String] = <function1>
val integerSorter = msort[Int]((a, b) => a < b) _
// integerSorter: List[Int] => List[Int] = <function1>
Those two invocation create two new functions taking only a single parameter - the list you want to sort. You don't have to tell it how to compare the elements, because you already did. Note you can invoke the same function with different lists as an argument.
integerSorter(List(2, 3, 1))
// res0: List[Int] = List(1, 2, 3)
integerSorter(List(2, 4, 1))
// res1: List[Int] = List(1, 2, 4)
stringSorter(List("b", "a", "c"))
res3: List[String] = List(a, b, c)
Note also that the newly created functions are type safe and following code will fail:
integerSorter(List("b", "a", "c"))
<console>:10: error: type mismatch;
found : String("b")
required: Int
integerSorter(List("b", "a", "c"))
Implicit Parameters
As the article in the link mentioned one of the reasons you may want to use Multiple Parameter Lists are implicit parameters.
When using implicit parameters, and you use the implicit keyword, it
applies to the entire parameter list. Thus, if you want only some
parameters to be implicit, you must use multiple parameter lists.
Let's modify the example code you gave us a bit to introduce a new type:
trait Comparator[T] {
def less(a: T, b: T): Boolean
}
and let's swap the parameter lists, and add implicit keyword to the second one, so now it becomes:
def msort[T](xs: List[T])(implicit c: Comparator[T]): List[T] = {
def merge(xs: List[T], ys: List[T], acc: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys.reverse ::: acc
case (_, Nil) => xs.reverse ::: acc
case (x :: xs1, y :: ys1) =>
if (c.less(x, y)) merge(xs1, ys, x :: acc)
else merge(xs, ys1, y :: acc)
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(ys)(c), msort(zs)(c), Nil).reverse
}
}
Now you can declare implicit object which will be used in case you don't supply one, e.g.
implicit val intComparator = new Comparator[Int] { def less(a: Int, b: Int) = a < b }
msort(List(5, 3, 1, 3))
// res8: List[Int] = List(1, 3, 3, 5)
While this may not seem to be very appealing it gives you extra flexibility when designing your API. Let's assume that we have a type called CustomType. It can declare an implicit in the companion object and it will be resolved "automatically" by the compiler.
case class CustomType(ordinal: Int, name: String)
object CustomType {
implicit val customTypeComparator = new Comparator[CustomType] {
def less(a: CustomType, b: CustomType) = a.ordinal < b.ordinal
}
}
msort(List(CustomType(2, "Second"), CustomType(1, "First")))
// res11: List[CustomType] = List(CustomType(1,First), CustomType(2,Second))
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T]
This function takes two arguments: a function less and a list xs.
How do I invoke msort?
You have to provide values for both arguments: msort(...)(...).
Why is a function required as part of the invocation?
Because the argument less is declared with function type (T, T) => Boolean.