diff --git a/03/practice/src/main/scala/org/spbsu/mkn/scala/MyGenericList.scala b/03/practice/src/main/scala/org/spbsu/mkn/scala/MyGenericList.scala index 0adc8a4..bd3efb5 100644 --- a/03/practice/src/main/scala/org/spbsu/mkn/scala/MyGenericList.scala +++ b/03/practice/src/main/scala/org/spbsu/mkn/scala/MyGenericList.scala @@ -1,20 +1,90 @@ package org.spbsu.mkn.scala import org.spbsu.mkn.scala.MyGenericList._ +import scala.annotation.tailrec -sealed trait MyGenericList { - def head: Int - def tail: MyGenericList - def drop(n: Int): MyGenericList - def take(n: Int): MyGenericList - def map(f: Int => Int): MyGenericList - def ::(elem: Int): MyGenericList = ??? +sealed trait MyGenericList[+T] { + def head: T + + def tail: MyGenericList[T] + + def drop(n: Int): MyGenericList[T] + + def take(n: Int): MyGenericList[T] + + def map[P >: T, S](f: P => S): MyGenericList[S] + + def ::[S >: T](elem: S): MyGenericList[S] = MyCons(elem, this) } -object MyGenericList { +case object MyGenericList { def undef: Nothing = throw new UnsupportedOperationException("operation is undefined") - def fromSeq(seq: Seq[Int]): MyGenericList = ??? - def size(intList: MyGenericList): Int = ??? - // extra task: implement sum using foldLeft - // def foldLeft(???)(???): ??? = ??? + + def fromSeq[T](seq: Seq[T]): MyGenericList[T] = seq.foldRight[MyGenericList[T]](MyNil) { (x, list) => MyCons(x, list) } + + def size[T](myList: MyGenericList[T]): Int = { + myList match { + case MyNil => 0 + case MyCons(_, list) => 1 + size(list) + } + } + + def sum[T: Numeric](myList: MyGenericList[T]): T = { + myList match { + case MyNil => undef + case _ => foldLeft(myList, Numeric[T].zero) { Numeric[T].plus } + } + } + + @tailrec + def foldLeft[T, S](myList: MyGenericList[T], initValue: S)(f: (S, T) => S): S = { + myList match { + case MyNil => initValue + case MyCons(x, list) => foldLeft(list, f(initValue, x))(f) + } + } +} + +case object MyNil extends MyGenericList[Nothing] { + override def head: Nothing = undef + + override def tail: MyGenericList[Nothing] = MyNil + + override def drop(n: Int): MyGenericList[Nothing] = { + n match { + case 0 => MyNil + case _ => undef + } + } + + override def take(n: Int): MyGenericList[Nothing] = { + n match { + case 0 => MyNil + case _ => undef + } + } + + override def map[T, S](f: T => S): MyGenericList[S] = MyNil +} + +case class MyCons[T](x: T, list: MyGenericList[T]) extends MyGenericList[T] { + override def head: T = x + + override def tail: MyGenericList[T] = list + + override def drop(n: Int): MyGenericList[T] = { + n match { + case 0 => this + case _ => list.drop(n - 1) + } + } + + override def take(n: Int): MyGenericList[T] = { + n match { + case 0 => MyNil + case _ => MyCons(x, list.take(n - 1)) + } + } + + override def map[P >: T, S](f: P => S): MyGenericList[S] = MyCons(f(x), list.map(f)) } diff --git a/03/practice/src/test/scala/org/spbsu/mkn/scala/MyGenericListTest.scala b/03/practice/src/test/scala/org/spbsu/mkn/scala/MyGenericListTest.scala index fe84c45..c8103df 100644 --- a/03/practice/src/test/scala/org/spbsu/mkn/scala/MyGenericListTest.scala +++ b/03/practice/src/test/scala/org/spbsu/mkn/scala/MyGenericListTest.scala @@ -5,49 +5,58 @@ import org.spbsu.mkn.scala.MyGenericList.{fromSeq, size, sum} class MyGenericListTest extends AnyFunSuite { - // remove after implementing actual MyNil - object MyNil - test("head") { - assert(fromSeq(Seq(1,2,3)).head == 1) + assert(fromSeq(Seq(1, 2, 3)).head == 1) assert(fromSeq(Seq(1)).head == 1) assertThrows[UnsupportedOperationException](fromSeq(Seq()).head) } test("tail") { - assert(fromSeq(Seq(1,2,3)).tail == fromSeq(Seq(2,3))) + assert(fromSeq(Seq(1, 2, 3)).tail == fromSeq(Seq(2, 3))) assert(fromSeq(Seq(1)).tail == MyNil) } test("drop") { - assert(fromSeq(Seq(1,2,3)).drop(0) == fromSeq(Seq(1,2,3))) - assert(fromSeq(Seq(1,2,3)).drop(2) == fromSeq(Seq(3))) - assert(fromSeq(Seq(1,2,3)).drop(3) == MyNil) - assertThrows[UnsupportedOperationException](fromSeq(Seq(1,2,3)).drop(10)) + assert(fromSeq(Seq(1, 2, 3)).drop(0) == fromSeq(Seq(1, 2, 3))) + assert(fromSeq(Seq(1, 2, 3)).drop(2) == fromSeq(Seq(3))) + assert(fromSeq(Seq(1, 2, 3)).drop(3) == MyNil) + assertThrows[UnsupportedOperationException](fromSeq(Seq(1, 2, 3)).drop(10)) } test("take") { - assert(fromSeq(Seq(1,2,3)).take(0) == MyNil) - assert(fromSeq(Seq(1,2,3)).take(2) == fromSeq(Seq(1,2))) - assert(fromSeq(Seq(1,2,3)).take(3) == fromSeq(Seq(1,2,3))) - assertThrows[UnsupportedOperationException](fromSeq(Seq(1,2,3)).take(10)) + assert(fromSeq(Seq(1, 2, 3)).take(0) == MyNil) + assert(fromSeq(Seq(1, 2, 3)).take(2) == fromSeq(Seq(1, 2))) + assert(fromSeq(Seq(1, 2, 3)).take(3) == fromSeq(Seq(1, 2, 3))) + assertThrows[UnsupportedOperationException](fromSeq(Seq(1, 2, 3)).take(10)) + } + + trait Animal { + val name: String + final val age = 1 } + case class Cat(override val name: String) extends Animal + case class Dog(override val name: String) extends Animal test("map") { - assert(MyNil.map(_ * 2) == MyNil) - assert(fromSeq(Seq(1,2,3)).map(_ * 2) == fromSeq(Seq(2,4,6))) - assert(fromSeq(Seq(1,2,3)).map(identity) == fromSeq(Seq(1,2,3))) + assert(MyNil.map[Int, Int](_ * 2) == MyNil) + assert(fromSeq(Seq(1, 2, 3)).map[Int, Int](_ * 2) == fromSeq(Seq(2, 4, 6))) + assert(fromSeq(Seq(1, 2, 3)).map[Int, Int](identity) == fromSeq(Seq(1, 2, 3))) + + assert(fromSeq(Seq('a', 'b', 'c')).map[Char, Int](x => 26 * (x - 'a')) == fromSeq(Seq(0, 26, 52))) + assert(fromSeq(Seq(Dog("Tuzik"), Cat("Alice"))).map[Animal, String](x => x.name) == fromSeq(Seq("Tuzik", "Alice"))) + assert(fromSeq(Seq[Animal](Dog("Bobik"), Dog("Tuzik"))).map[Animal, Int](x => x.age) == fromSeq(Seq(1, 1))) } test("size") { assert(size(MyNil) == 0) - assert(size(fromSeq(Seq(1,2,3))) == 3) + assert(size(fromSeq(Seq(1, 2, 3))) == 3) } test("sum") { - assertThrows[UnsupportedOperationException](sum(MyNil)) - assert(sum(fromSeq(Seq(1,2,3))) == 6) + assertThrows[UnsupportedOperationException](sum[Int](MyNil)) + assert(sum(fromSeq(Seq(1, 2, 3))) == 6) assert(sum(fromSeq(Seq(1))) == 1) - } -} + assert(sum(fromSeq(Seq(' ', '0'))) == 'P') + } +} \ No newline at end of file