Related
Suppose I am writing a function to zip two lists only if they are of the same size and fail otherwise:
def foo(xs: List[Int], ys: List[Int]): Either[String, List[(Int, Int)]] =
if (xs.size == ys.size) Right(xs zip ys) else Left(s"$xs and $ys have different sizes")
It works but I don't like using if. How would you improve the code above ?
You can use pattern matching if for some reason you don't like if
def zip(as: List[Int], bs: List[Int]): Either[String, List[(Int, Int)]] = (as, bs) match {
case (Nil, Nil) => Right(Nil)
case (a :: as1, b :: bs1) => for {
t <- zip(as1, bs1)
} yield (a, b) :: t
case _ => Left(s"$as and $bs have different sizes")
}
or
def zip(as: List[Int], bs: List[Int]): Either[String, List[(Int, Int)]] = {
#tailrec
def loop(as1: List[Int], bs1: List[Int], acc: List[(Int, Int)]): Either[String, List[(Int, Int)]] = (as1, bs1) match {
case (Nil, Nil) => Right(acc)
case (a :: as2, b :: bs2) => loop(as2, bs2, (a, b) :: acc)
case _ => Left(s"$as and $bs have different sizes")
}
loop(as, bs, Nil).map(_.reverse)
}
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)
}
I want to write a function which will add an element n to a list n times for all elements in an input list.
For example:
L = List(2,4,1)
Output should be:
List(2,2,4,4,4,4,1)
I would like to do it with tail recursion. So far, I have written this:
def repeat(numbers: List[Int]): List[Int] = {
def repeat_acc(numbers: List[Int], acc: List[Int], number_acc: Int): List[Int] = {
if (numbers.length == 0)
return acc
else if (number_acc == 0)
repeat_acc(numbers.tail, acc, numbers.head)
else
repeat_acc(numbers, acc :+ numbers.head, (number_acc-1))
}
repeat_acc(numbers, List(), 0)
}
The problem is that it leaves out the first element of the list. For this output will be:
(4, 4, 1, 1, 1, 1)
I know why it happens, but I cannot fix it. I have tried many other ways, but for me it seems that tail recursion do not work here. Some always goes wrong, and I get the wrong result.
Thanks for any advice.
I know you want to do a personalized tail recursive call, but I would recommend the following instead:
def repeat(numbers: List[Int]): List[Int] = {
numbers.flatMap(n => List.fill(n)(n))
}
The inner function takes the value n and repeats it n times in a list, and then this function is flat mapped onto the original list (a regular map would turn List(1,2,3) into List(List(1), List(2, 2), List(3, 3, 3)), so we use flat map). This has the benefit of 'doing it the Scala way' with built in collections functionality.
This problem gets a lot simpler if you add another parameter to your inner recursive method, representing the current number being added. You should also familiarize yourself with match statements, since they are really powerful in Scala and can help express exactly this type of logic. Nested if/else statements and early return statements are considered unidiomatic. Try:
def repeat(numbers: List[Int]) = {
def repeatAcc(acc: List[Int], curr: Int, rem: Int, numbers:List[Int]): List[Int] =
(numbers, rem) match {
case (Nil, 0) => acc
case (hd::tl, 0) => repeatAcc(acc, hd, hd, tl)
case (_, n) => repeatAcc(acc :+ curr, curr, n - 1, numbers)
}
repeatAcc(List.empty[Int], 0, 0, numbers)
}
You might also try to use some standard Scala methods like List.fill, which can be used in conjunction with the tail recursion as follows:
def repeat(numbers: List[Int]) = {
def repeatAcc(acc: List[Int], numbers:List[Int]): List[Int] = numbers match {
case hd::tl => repeatAcc(acc ++ List.fill(hd)(hd), tl)
case Nil => acc
}
repeatAcc(List.empty[Int], numbers)
}
Finally, I understand you're trying to learn about these core concepts, but it should be mentioned that this is really simple with Scala built ins:
(List.empty[Int] /: numbers) { case (soFar, next) => soFar ++ List.fill(next)(next) }
numbers.flatMap(x => List.fill(x)(x))
Try this, it works
import scala.annotation.tailrec
#tailrec
def f(l: List[Int], res: List[Int] = Nil): List[Int] = {
#tailrec
def g(n: Int, acc: List[Int]): List[Int] = {
if (n == 1) acc
else g(n - 1, acc :+ acc.head)
}
if (l == Nil) res else f(l.tail, res ++ g(l.head, List(l.head)))
}
scala> f(List(1,2,3))
res0: List[Int] = List(1, 2, 2, 3, 3, 3)
Ok, I find the solution. Looks bad, but works. I'm too tired to figure out something better.
Here is my code:
def repeat(numbers: List[Int]): List[Int] = {
def repeat_acc(numbers: List[Int], acc: List[Int], number_acc: Int): List[Int] = {
if (numbers.length == 1 && number_acc == 0)
return acc
else if (acc.length == 0)
repeat_acc(numbers, acc :+ numbers.head, (number_acc-1))
else if (number_acc == 0 && numbers.tail != Nil ){
repeat_acc(numbers.tail, acc, (numbers.tail.head))
}
else
repeat_acc(numbers, acc :+ numbers.head, (number_acc-1))
}
repeat_acc(numbers, List(), numbers.head)
}
simple, just use fill + flatMap
val result = L.flatMap(x => List.fill(x)(x))
for recursive solution :
scala> def repeat(numbers: List[Int]): List[Int] = {
def run(nums:List[Int]):List[Int] ={
nums match {
case Nil => List.empty[Int]
case x::Nil => List.fill(x)(x)
case x::xs => List.fill(x)(x):::run(xs)
}}
run(numbers)
}
scala> repeat(List(2,4,1))
res1: List[Int] = List(2, 2, 4, 4, 4, 4, 1)
I start learning Scala and I don't quite understand some behaviors of pattern-matching. Can anyone explain to me why the first case works but the second case doesn't work?
1
def getFirstElement(list: List[Int]) : Int = list match {
case h::tail => h
case _ => -1
}
Scala> getFirstElement(List(1,2,3,4))
res: Int = 1
Scala> 1 :: List(1,2)
res: List[Int] = List(1, 1, 2)
2
def getSumofFirstTwoElement(list: List[Int]): Int = list match {
case List(a: Int, b: Int)++tail => a + b
case _ => -1
}
<console>:11: error: not found: value ++
Scala> List(1,2) ++ List(3,4,5)
res: List[Int] = List(1, 2, 3, 4, 5)
The reason is that there is no unapply method on an object of type ++. The reason that :: works is because behind the scenes it is really:
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
override def tail : List[B] = tl
override def isEmpty: Boolean = false
}
Source
This leads to how pattern matching is implemented in Scala. It uses extractors (or here), which are basically objects that contain an unapply method, which is provided by default with case classes.
++ is a method on object of list. I think you want:
def getSumofFirstTwoElement(list: List[Int]): Int = list match {
case a::b::tail => a + b
case _ => -1
}
In case two, ++ isn't used for unapply. You want the extractor :: to decompose the list.
A good explanation here.
scala> def getSumofFirstTwoElement(list: List[Int]): Int = list match {
| case a::b::t => a + b
| case _ => -1
| }
getSumofFirstTwoElement: (list: List[Int])Int
scala> getSumofFirstTwoElement(List(1,2,3,4))
res0: Int = 3
This is a simple exercise I am solving in Scala: given a list l return a new list, which contains every n-th element of l. If n > l.size return an empty list.
def skip(l: List[Int], n: Int) =
Range(1, l.size/n + 1).map(i => l.take(i * n).last).toList
My solution (see above) seem to work but I am looking for smth. simpler. How would you simplify it?
Somewhat simpler:
scala> val l = (1 to 10).toList
l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// n == 3
scala> l.drop(2).grouped(3).map(_.head).toList
res0: List[Int] = List(3, 6, 9)
// n > l.length
scala> l.drop(11).grouped(12).map(_.head).toList
res1: List[Int] = List()
(the toList just to force the iteratot to be evaluated)
Works with infinite lists:
Stream.from(1).drop(2).grouped(3).map(_.head).take(4).toList
res2: List[Int] = List(3, 6, 9, 12)
scala> def skip[A](l:List[A], n:Int) =
l.zipWithIndex.collect {case (e,i) if ((i+1) % n) == 0 => e} // (i+1) because zipWithIndex is 0-based
skip: [A](l: List[A], n: Int)List[A]
scala> val l = (1 to 10).toList
l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> skip(l,3)
res2: List[Int] = List(3, 6, 9)
scala> skip(l,11)
res3: List[Int] = List()
A bit more readable and the loop size is O(l.length/n):
def skip(l: List[Int], n: Int) = {
require(n > 0)
for (step <- Range(start = n - 1, end = l.length, step = n))
yield l(step)
}
Fold left approach O(n)
def skip(xs: List[Int], n: Int) = {
xs.foldLeft((List[Int](), n)){ case ((acc, counter), x) =>
if(counter==1)
(x+:acc,n)
else
(acc, counter-1)
}
._1
.reverse
}
scala > skip(List(1,2,3,4,5,6,7,8,9,10), 3)
Tailrec less readable approach O(n)
import scala.annotation.tailrec
def skipTR(xs: List[Int], n: Int) = {
#tailrec
def go(ys: List[Int], acc: List[Int], counter: Int): List[Int] = ys match {
case k::ks=>
if(counter==1)
go(ks, k+:acc , n)
else
go(ks, acc, counter-1)
case Nil => acc
}
go(xs, List(), n).reverse
}
skipTR(List(1,2,3,4,5,6,7,8,9,10), 3)
You could omit toList if you don't mind an iterator:
scala> def skip[A](l:List[A], n:Int) =
l.grouped(n).filter(_.length==n).map(_.last).toList
skip: [A](l: List[A], n: Int)List[A]
scala> skip (l,3)
res6: List[Int] = List(3, 6, 9)
Two approaches based in filter on indexes, as follows,
implicit class RichList[A](val list: List[A]) extends AnyVal {
def nthA(n: Int) = n match {
case 0 => List()
case _ => (1 to a.size).filter( _ % n == 0).map { i => list(i-1)}
}
def nthB(n: Int) = n match {
case 0 => List()
case _ => list.zip(Stream.from(1)).filter(_._2 % n == 0).unzip._1
}
}
and so for a given list
val a = ('a' to 'z').toList
we have that
a.nthA(5)
res: List(e, j, o, t, y)
a.nthA(123)
res: List()
a.nthA(0)
res: List()
Update
Using List.tabulate as follows,
implicit class RichList[A](val list: List[A]) extends AnyVal {
def nthC(n: Int) = n match {
case 0 => List()
case n => List.tabulate(list.size) {i =>
if ((i+1) % n == 0) Some(list(i))
else None }.flatten
}
}