Skip to content

Commit

Permalink
Complete up to P20
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhijit Sarkar committed Jan 4, 2024
1 parent f064049 commit 7d89649
Show file tree
Hide file tree
Showing 24 changed files with 341 additions and 15 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,25 @@

[P10](src/main/scala/list/P10.scala) (*) Run-length encoding of a list.

P11 (*) Modified run-length encoding.
[P11](src/main/scala/list/P11.scala) (*) Modified run-length encoding.

P12 (**) Decode a run-length encoded list.
[P12](src/main/scala/list/P12.scala) (**) Decode a run-length encoded list.

P13 (**) Run-length encoding of a list (direct solution).
[P13](src/main/scala/list/P13.scala) (**) Run-length encoding of a list (direct solution).

P14 (*) Duplicate the elements of a list.
[P14](src/main/scala/list/P14.scala) (*) Duplicate the elements of a list.

P15 (**) Duplicate the elements of a list a given number of times.
[P15](src/main/scala/list/P15.scala) (**) Duplicate the elements of a list a given number of times.

P16 (**) Drop every Nth element from a list.
[P16](src/main/scala/list/P16.scala) (**) Drop every Nth element from a list.

P17 (*) Split a list into two parts.
[P17](src/main/scala/list/P17.scala) (*) Split a list into two parts.

P18 (**) Extract a slice from a list.
[P18](src/main/scala/list/P18.scala) (**) Extract a slice from a list.

P19 (**) Rotate a list N places to the left.
[P19](src/main/scala/list/P19.scala) (**) Rotate a list N places to the left.

P20 (*) Remove the Kth element from a list.
[P20](src/main/scala/list/P20.scala) (*) Remove the Kth element from a list.

P21 (*) Insert an element at a given position into a list.

Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/list/P03.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ object P03:
def nth[A](n: Int, l: List[A]): A = l match
case x :: _ if n == 0 => x
case x :: tail => nth(n - 1, tail)
case Nil => throw NoSuchElementException("n larger than list size")
case Nil => throw NoSuchElementException("n too large")
10 changes: 6 additions & 4 deletions src/main/scala/list/P07.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ package list
// res0: List[Any] = List(1, 1, 2, 3, 5, 8)

object P07:
def flatten(l: List[Any]): List[Any] = l.foldRight(Nil)(flatten)
def flatten[A](l: List[A | List[A]]): List[A] = l.foldRight(List.empty[A])(flatten)

private def flatten(x: Any, acc: List[Any]): List[Any] =
private def flatten[A](x: A | List[A], acc: List[A]): List[A] =
// Can't check for instanceOf(List[A]) since
// A vanishes at runtime (type erasure).
if x.isInstanceOf[List[Any]]
then x.asInstanceOf[List[Any]].foldRight(acc)(flatten)
else x :: acc
then x.asInstanceOf[List[A]].foldRight(acc)(flatten)
else x.asInstanceOf[A] :: acc
1 change: 1 addition & 0 deletions src/main/scala/list/P10.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ package list
object P10:

def encode[A](l: List[A]): List[(Int, A)] =
// size takes O(n) time on lists.
P09.pack(l).map(xs => (xs.size, xs.head))
18 changes: 18 additions & 0 deletions src/main/scala/list/P11.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package list

// P11 (*) Modified run-length encoding.
// Modify the result of problem P10 in such a way that if an element has no
// duplicates it is simply copied into the result list. Only elements with
// duplicates are transferred as (N, E) terms.
//
// Example:
// scala> encodeModified(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
// res0: List[Any] = List((4,'a), 'b, (2,'c), (2,'a), 'd, (4,'e))

object P11:

def encodeModified[A](l: List[A]): List[A | (Int, A)] =
P09
.pack(l)
// size takes O(n) time on lists.
.map(xs => if xs.tail.isEmpty then xs.head else (xs.size, xs.head))
14 changes: 14 additions & 0 deletions src/main/scala/list/P12.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package list

// P12 (**) Decode a run-length encoded list.
// Given a run-length code list generated as specified in problem P10,
// construct its uncompressed version.
//
// Example:
// scala> decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
// res0: List[Symbol] = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)

object P12:

def decode[A](l: List[(Int, A)]): List[A] =
l.flatMap((len, x) => List.fill(len)(x))
18 changes: 18 additions & 0 deletions src/main/scala/list/P13.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package list

// P13 (**) Run-length encoding of a list (direct solution).
// Implement the so-called run-length encoding data compression method
// directly. I.e. don't use other methods you've written (like P09's
// pack); do all the work directly.
//
// Example:
// scala> encodeDirect(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
// res0: List[(Int, Symbol)] = List((4,'a), (1,'b), (2,'c), (2,'a), (1,'d), (4,'e))

object P13:

def encodeDirect[A](l: List[A]): List[(Int, A)] =
l.foldRight(List.empty[(Int, A)]):
case (x, Nil) => List((1, x))
case (x, acc @ ((_, y) :: _)) if x != y => (1, x) :: acc
case (_, (n, y) :: ys) => (n + 1, y) :: ys
11 changes: 11 additions & 0 deletions src/main/scala/list/P14.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package list

// P14 (*) Duplicate the elements of a list.
// Example:
// scala> duplicate(List('a, 'b, 'c, 'c, 'd))
// res0: List[Symbol] = List('a, 'a, 'b, 'b, 'c, 'c, 'c, 'c, 'd, 'd)

object P14:

def duplicate[A](l: List[A]): List[A] =
l.flatMap(x => List(x, x))
11 changes: 11 additions & 0 deletions src/main/scala/list/P15.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package list

// P15 (**) Duplicate the elements of a list a given number of times.
// Example:
// scala> duplicateN(3, List('a, 'b, 'c, 'c, 'd))
// res0: List[Symbol] = List('a, 'a, 'a, 'b, 'b, 'b, 'c, 'c, 'c, 'c, 'c, 'c, 'd, 'd, 'd)

object P15:

def duplicateN[A](n: Int, l: List[A]): List[A] =
l.flatMap(x => List.fill(n)(x))
16 changes: 16 additions & 0 deletions src/main/scala/list/P16.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package list

// P16 (**) Drop every Nth element from a list.
// Example:
// scala> drop(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
// res0: List[Symbol] = List('a, 'b, 'd, 'e, 'g, 'h, 'j, 'k)

object P16:

def drop[A](n: Int, l: List[A]): List[A] =
// Other ways:
// xs.withFilter(...).map(...)
// for (..) yield
// xs.flatMap(...)
l.zipWithIndex.collect:
case (x, i) if (i + 1) % n != 0 => x
16 changes: 16 additions & 0 deletions src/main/scala/list/P17.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package list

// P17 (*) Split a list into two parts.
// The length of the first part is given. Use a Tuple for your result.
//
// Example:
// scala> split(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
// res0: (List[Symbol], List[Symbol]) = (List('a, 'b, 'c),List('d, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
object P17:

def split[A](n: Int, l: List[A]): (List[A], List[A]) = l match
case Nil => (Nil, Nil)
case xs if n == 0 => (Nil, xs)
case x :: xs =>
split((n - 1), xs) match
case (left, right) => (x :: left, right)
16 changes: 16 additions & 0 deletions src/main/scala/list/P18.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package list

// P18 (**) Extract a slice from a list.
// Given two indices, I and K, the slice is the list containing the elements
// from and including the Ith element up to but not including the Kth
// element of the original list. Start counting the elements with 0.
//
// Example:
// scala> slice(3, 7, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
// res0: List[Symbol] = List('d, 'e, 'f, 'g)
object P18:

def slice[A](start: Int, end: Int, l: List[A]): List[A] =
if end < start
then throw new IllegalArgumentException("invalid bounds")
else P17.split(start, l)(1).take(end - start)
16 changes: 16 additions & 0 deletions src/main/scala/list/P19.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package list

// P19 (**) Rotate a list N places to the left.
// Examples:
// scala> rotate(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
// res0: List[Symbol] = List('d, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'a, 'b, 'c)
//
// scala> rotate(-2, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
// res1: List[Symbol] = List('j, 'k, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i)

object P19:

def rotate[A](n: Int, l: List[A]): List[A] =
val k = Math.floorMod(n, l.size)
val (left, right) = P17.split(k, l)
right ++ left
16 changes: 16 additions & 0 deletions src/main/scala/list/P20.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package list

// P20 (*) Remove the Kth element from a list.
// Return the list and the removed element in a Tuple. Elements are
// numbered from 0.
//
// Example:
// scala> removeAt(1, List('a, 'b, 'c, 'd))
// res0: (List[Symbol], Symbol) = (List('a, 'c, 'd),'b)

object P20:

def removeAt[A](n: Int, l: List[A]): (List[A], A) =
P17.split(n, l) match
case (_, Nil) => throw new NoSuchElementException("n too large")
case (left, right) => (left ++ right.tail, right.head)
21 changes: 21 additions & 0 deletions src/test/scala/list/P11Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package list

import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAllNoShrink
import org.scalacheck.Gen

class P11Suite extends ScalaCheckSuite:

property("modified run-length encoding"):
forAllNoShrink(Gen.listOf(Gen.choose(1, 5))) { (lst: List[Int]) =>
val xs = lst.flatMap(n => List.fill(n)(n))
val obtained = P11.encodeModified(xs)
val expected = xs
.foldRight(List.empty[(Int, Int)])((x, acc) =>
if (acc.isEmpty || acc.head(1) != x)
then (1, x) :: acc
else (acc.head(0) + 1, acc.head(1)) :: acc.tail
)
.map((len, x) => if len == 1 then x else (len, x))
assertEquals(obtained, expected)
}
15 changes: 15 additions & 0 deletions src/test/scala/list/P12Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package list

import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAllNoShrink
import org.scalacheck.Gen

class P12Suite extends ScalaCheckSuite:

property("decode run-length encoded list"):
forAllNoShrink(Gen.listOf(Gen.choose(1, 5))) { (lst: List[Int]) =>
val xs = lst.map(x => (x, x))
val obtained = P12.decode(xs)
val expected = lst.flatMap(x => List.fill(x)(x))
assertEquals(obtained, expected)
}
15 changes: 15 additions & 0 deletions src/test/scala/list/P13Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package list

import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAllNoShrink
import org.scalacheck.Gen

class P13Suite extends ScalaCheckSuite:

property("run-length encoding direct"):
forAllNoShrink(Gen.listOf(Gen.choose(1, 5))) { (lst: List[Int]) =>
val xs = lst.flatMap(n => List.fill(n)(n))
val obtained = P13.encodeDirect(xs)
val expected = P10.encode(xs)
assertEquals(obtained, expected)
}
14 changes: 14 additions & 0 deletions src/test/scala/list/P14Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package list

import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAllNoShrink
import org.scalacheck.Gen

class P14Suite extends ScalaCheckSuite:

property("duplicate the elements"):
forAllNoShrink(Gen.listOf(ListGen.genInt)) { (lst: List[Int]) =>
val obtained = P14.duplicate(lst)
val expected = lst.flatMap(n => List.fill(2)(n))
assertEquals(obtained, expected)
}
16 changes: 16 additions & 0 deletions src/test/scala/list/P15Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package list

import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAllNoShrink
import org.scalacheck.Gen
import scala.util.Random

class P15Suite extends ScalaCheckSuite:

property("duplicate the elements N times"):
forAllNoShrink(Gen.listOf(ListGen.genInt)) { (lst: List[Int]) =>
val n = Random.between(1, 5)
val obtained = P15.duplicateN(n, lst)
val expected = lst.flatMap(x => List.fill(n)(x))
assertEquals(obtained, expected)
}
20 changes: 20 additions & 0 deletions src/test/scala/list/P16Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package list

import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAllNoShrink
import org.scalacheck.Gen
import scala.util.Random

class P16Suite extends ScalaCheckSuite:

property("drop every Nth element"):
forAllNoShrink(Gen.listOf(ListGen.genInt)) { (lst: List[Int]) =>
val n =
if lst.isEmpty
then 0
else Random.between(1, lst.size + 1)
val obtained = P16.drop(n, lst)
// guard in for-comprehension is sugar for withFilter.
val expected = for ((x, i) <- lst.zipWithIndex if (i + 1) % n != 0) yield x
assertEquals(obtained, expected)
}
19 changes: 19 additions & 0 deletions src/test/scala/list/P17Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package list

import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAllNoShrink
import org.scalacheck.Gen
import scala.util.Random

class P17Suite extends ScalaCheckSuite:

property("split a list into two parts"):
forAllNoShrink(Gen.listOf(ListGen.genInt)) { (lst: List[Int]) =>
val n =
if lst.isEmpty
then 0
else Random.between(1, lst.size + 1)
val obtained = P17.split(n, lst)
val expected = lst.splitAt(n)
assertEquals(obtained, expected)
}
20 changes: 20 additions & 0 deletions src/test/scala/list/P18Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package list

import munit.ScalaCheckSuite
import org.scalacheck.Prop.forAllNoShrink
import org.scalacheck.Gen
import scala.util.Random

class P18Suite extends ScalaCheckSuite:

property("extract a slice"):
forAllNoShrink(Gen.listOf(ListGen.genInt)) { (lst: List[Int]) =>
val end =
if lst.isEmpty
then 0
else Random.between(1, lst.size + 1)
val start = if end == 0 then 0 else Random.between(0, end)
val obtained = P18.slice(start, end, lst)
val expected = lst.splitAt(start)._2.take(end - start)
assertEquals(obtained, expected, s"start=${start}, end=${end}")
}
15 changes: 15 additions & 0 deletions src/test/scala/list/P19Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package list

import munit.FunSuite

class P19Suite extends FunSuite:

test("rotate a list N places to the left"):
val xs = ('a' to 'k').toList
val obtained1 = P19.rotate(3, xs)
val expected1 = List('d', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'a', 'b', 'c')
assertEquals(obtained1, expected1)

val obtained2 = P19.rotate(-2, xs)
val expected2 = List('j', 'k', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')
assertEquals(obtained2, expected2)
Loading

0 comments on commit 7d89649

Please sign in to comment.