Skip to content

Commit

Permalink
homework 5
Browse files Browse the repository at this point in the history
  • Loading branch information
isolomein-naumen committed Oct 29, 2023
1 parent 0111c3f commit e7c65d4
Show file tree
Hide file tree
Showing 10 changed files with 341 additions and 1 deletion.
14 changes: 14 additions & 0 deletions homeworks/homework_5/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ThisBuild / version := "0.1.0-SNAPSHOT"

ThisBuild / scalaVersion := "2.12.10"

libraryDependencies += "com.lihaoyi" %% "utest" % "0.5.3" % "test"

libraryDependencies += "org.typelevel" %% "cats-core" % "2.10.0"

testFrameworks += new TestFramework("utest.runner.Framework")

lazy val root = (project in file("."))
.settings(
name := "homework_5"
)
11 changes: 11 additions & 0 deletions homeworks/homework_5/homework_5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Домашнее задание №5 (курс Scala, Naumen)

Необходимо выполнить задания, описанные в:
1. <a href='https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_5/src/main/scala'>src/main/scala/Task1.scala</a>
2. <a href='https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_5/src/main/scala'>src/main/scala/Task2.scala</a>
3. <a href='https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_5/src/main/scala'>src/main/scala/Task3.scala</a>
4. <a href='https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_5/src/main/scala'>src/main/scala/Task4.scala</a>
5. <a href='https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_5/src/main/scala'>src/main/scala/Task5.scala</a>

Решение должно успешно проходить тесты в <a href='https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_5/src/test/scala'>(src/test/scala/)</a>

32 changes: 32 additions & 0 deletions homeworks/homework_5/src/main/scala/Task3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import cats._
import cats.implicits._

import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt

/*
Задание №3
Всё просто, нужно посчитать количество строк.
Реализуйте функцию countWords, которая принимает список строк.
Обязательно использовать функцию mapReduce.
*/
object Task3 extends App {
def mapReduce[A, B: Monoid](values: Vector[A])(func: A => B): Future[B] = {
val numCores = Runtime.getRuntime.availableProcessors
val groupSize = (1.0 * values.size / numCores).ceil.toInt
values
.grouped(groupSize)
.toVector
.traverse(group => Future(group.foldMap(func)))
.map(_.combineAll)
}

case class Count(word: String, count: Int)
case class WordsCount(count: Seq[Count])
object WordsCount {
implicit val monoid: Monoid[WordsCount] = ???
}

def countWords(lines: Vector[String]): WordsCount = ???
}
47 changes: 47 additions & 0 deletions homeworks/homework_5/src/main/scala/Task4.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import scala.language.higherKinds
import scala.util.{Failure, Success, Try}

/*
Задание №4
Давайте реализуем свою монаду для обработки ошибок.
Нужно:
1) Реализовать функцию map в трейте MonadError
2) Написать инстанс MonadError для EIO
3) Реализовать функцию possibleError для обработки кода, который может вызывать ошибку
Примеры использования можно посмотреть в тестах.
Подсказка: На Either определён flatMap, его можно переиспользовать
*/
object Task4 extends App {
trait MonadError[F[_, _], E] {
def pure[A](value: A): F[E, A]
def flatMap[A, B](fa: F[E, A])(f: A => F[E, B]): F[E, B]

def map[A, B](fa: F[E, A])(f: A => B): F[E, B] = ???

def raiseError[A](fa: F[E, A])(error: => E): F[E, A]
def handleError[A](fa: F[E, A])(handle: E => A): F[E, A]
}

case class EIO[+E, +A](value: Either[E, A])
object EIO {
def apply[A](value: A): EIO[Nothing, A] = EIO[Nothing, A](Right(value))

def error[E, A](error: E): EIO[E, A] = EIO[E, A](Left(error))

def possibleError[A](f: => A): EIO[Throwable, A] = ???

implicit def monad[E]: MonadError[EIO, E] = ???
}

object EIOSyntax {
implicit class EIOOps[E, A](val eio: EIO[E, A]) {
def flatMap[B](f: A => EIO[E, B]): EIO[E, B] =
EIO.monad[E].flatMap(eio)(f)

def map[B](f: A => B): EIO[E, B] = EIO.monad.map(eio)(f)

def handleError(f: E => A): EIO[E, A] =
EIO.monad.handleError(eio)(f)
}
}
}
36 changes: 36 additions & 0 deletions homeworks/homework_5/src/main/scala/Task5.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import scala.util.{Failure, Success, Try}

/*
Задание №5
Задание аналогично предыдущему задания, но теперь мы уходим от использования стандартного Either.
Нужно:
1) Доделать реализацию MyEither (нужны аналоги Right и Left)
2) Написать для MyEither инстанс MonadError
3) Написать функции apply, error, possibleError
*/
object Task5 extends App {
import Task4.MonadError

sealed trait MyEither[+E, +A] {
def isError: Boolean
}
object MyEither {
def apply[A](value: A): MyEither[Nothing, A] = ???
def error[E, A](error: E): MyEither[E, A] = ???
def possibleError[A](f: => A): MyEither[Throwable, A] = ???

implicit def myEitherMonad[E]: MonadError[MyEither, E] = ???
}

object MyEitherSyntax {
implicit class MyEitherOps[E, A](val either: MyEither[E, A]) {
def flatMap[B](f: A => MyEither[E, B]): MyEither[E, B] =
MyEither.myEitherMonad[E].flatMap(either)(f)

def map[B](f: A => B): MyEither[E, B] = MyEither.myEitherMonad.map(either)(f)

def handleError(f: E => A): MyEither[E, A] =
MyEither.myEitherMonad.handleError(either)(f)
}
}
}
46 changes: 46 additions & 0 deletions homeworks/homework_5/src/test/scala/Test2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import cats.implicits._
import utest._

import scala.util.Random

object Test2 extends TestSuite {
override def tests: Tests = Tests {
import Task2._

'vectorsMustSum - (1 to 5).foreach { _ =>
val xs = Vector.fill(50)(Random.nextInt)
val ys = Vector.fill(50)(Random.nextInt)
val vectors = xs.zip(ys).map { case (x, y) => RadiusVector(x, y) }
val resultVector = RadiusVector(xs.sum, ys.sum)
assert(vectors.combineAll == resultVector)
}
'angleMustSum - (1 to 5).foreach { _ =>
val angles = Vector.fill(50)(DegreeAngle(Random.nextInt))
val result = angles.map(_.angel).sum % 360
assert(angles.combineAll == DegreeAngle(result))
}
'matrixMustSum - (1 to 5).foreach { _ =>
val firstRow = List.fill(50)(List.fill(3)(Random.nextInt))
val secondRow = List.fill(50)(List.fill(3)(Random.nextInt))
val thirdRow = List.fill(50)(List.fill(3)(Random.nextInt))
val matrixs = firstRow.zip(secondRow.zip(thirdRow)).map {
case (x1 :: y1 :: z1 :: Nil, (x2 :: y2 :: z2 :: Nil, x3 :: y3 :: z3 :: Nil)) => SquareMatrix(
((x1, y1, z1), (x2, y2, z2), (x3, y3, z3))
)
}
def countRow(row: List[List[Int]]): (Int, Int, Int) =
row.foldLeft((0, 0, 0)) { (acc, next) =>
next match {
case x :: y :: z :: Nil => (acc._1 + x, acc._2 + y, acc._3 + z)
case _ => throw new Exception()
}
}
val result = SquareMatrix((
countRow(firstRow),
countRow(secondRow),
countRow(thirdRow)
))
assert(matrixs.combineAll == result)
}
}
}
21 changes: 21 additions & 0 deletions homeworks/homework_5/src/test/scala/Test3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import utest._

import scala.util.Random

object Test3 extends TestSuite {
override def tests: Tests = Tests {
import Task3._

'wordsMustBeCounted - (1 to 5).foreach { _ =>
val words = Vector.fill(100)(java.util.UUID.randomUUID().toString.replace("-", ""))
val counts = Vector.fill(100)(Random.nextInt(200)).map(_ + 1) // исключаем 0
val expected = WordsCount(words.zip(counts).map { case (w, c) => Count(w, c) })

val allWords = words.zip(counts).flatMap { case (word, count) => Vector.fill(count)(word) }
val lines = Random.shuffle(allWords).grouped(50).map(_.mkString(" ")).toVector
val result = countWords(lines)

expected.count.foreach(x => assert(result.count.contains(x)))
}
}
}
66 changes: 66 additions & 0 deletions homeworks/homework_5/src/test/scala/Test4.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import utest._

import scala.util.Random

object Test4 extends TestSuite {
override def tests: Tests = Tests {
import Task4._
import Task4.EIOSyntax._

'leftIdentityLow - {
assert(EIO(0).flatMap(x => EIO(x + 1)) == EIO(1))
}
'rightIdentityLow - {
assert(EIO(0).flatMap(x => EIO.apply(x)) == EIO(0))
}
'associativityLow - {
val f: Int => EIO[Nothing, Int] = x => EIO(x + 1)
val g: Int => EIO[Nothing, Int] = x => EIO(x * 10)
assert(EIO(0).flatMap(f).flatMap(g) == EIO(0).flatMap(x => f(x).flatMap(g)))
}
'usage - {
'simpleUsage - {
val (x, y, z) = (Random.nextInt(), Random.nextInt(), Random.nextInt())
val p = for {
a <- EIO(x)
c <- EIO(y)
d <- EIO(z)
} yield a + c + d
assert(p == EIO(Right(x + y + z)))
}
'errorUsage - {
val p = for {
a <- EIO(0)
_ <- EIO.error[String, Int]("Error")
} yield a
assert(p == EIO(Left("Error")))
}
'possibleErrorUsage - {
val p = for {
a <- EIO(12)
b <- EIO.possibleError(12 / 0)
} yield a + b
assert(p.value.isLeft)
}
'recoverErrorUsage - {
val (x, y, z) = (Random.nextInt(), Random.nextInt(), Random.nextInt())
val p = for {
a <- EIO(x)
b <- EIO.possibleError(y / 0).handleError(_ => y)
c <- EIO(z)
} yield a + b + c

assert(p == EIO(Right(x + y + z)))
}
'skippingErrorStopsEvaluation - {
val p = for {
a <- EIO(0)
v <- EIO.possibleError(12 / 0)
b <- EIO.possibleError(32 / 12).handleError(_ => 1)
c <- EIO(2)
} yield a + b + c + v
assert(p.value.isLeft)
}
}
}
}
66 changes: 66 additions & 0 deletions homeworks/homework_5/src/test/scala/Test5.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import utest._

import scala.util.Random

object Test5 extends TestSuite {
override def tests: Tests = Tests {
import Task5._
import Task5.MyEitherSyntax._

'leftIdentityLow - {
assert(MyEither(0).flatMap(x => MyEither(x + 1)) == MyEither(1))
}
'rightIdentityLow - {
assert(MyEither(0).flatMap(x => MyEither.apply(x)) == MyEither(0))
}
'associativityLow - {
val f: Int => MyEither[Nothing, Int] = x => MyEither(x + 1)
val g: Int => MyEither[Nothing, Int] = x => MyEither(x * 10)
assert(MyEither(0).flatMap(f).flatMap(g) == MyEither(0).flatMap(x => f(x).flatMap(g)))
}
'usage - {
'simpleUsage - {
val (x, y, z) = (Random.nextInt(), Random.nextInt(), Random.nextInt())
val p = for {
a <- MyEither(x)
c <- MyEither(y)
d <- MyEither(z)
} yield a + c + d
assert(p == MyEither(x + y + z))
}
'errorUsage - {
val p = for {
a <- MyEither(0)
_ <- MyEither.error[String, Int]("Error")
} yield a
assert(p.isError)
}
'possibleErrorUsage - {
val p = for {
a <- MyEither(12)
b <- MyEither.possibleError(12 / 0)
} yield a + b
assert(p.isError)
}
'recoverErrorUsage - {
val (x, y, z) = (Random.nextInt(), Random.nextInt(), Random.nextInt())
val p = for {
a <- MyEither(x)
b <- MyEither.possibleError(y / 0).handleError(_ => y)
c <- MyEither(z)
} yield a + b + c

assert(p == MyEither(x + y + z))
}
'skippingErrorStopsEvaluation - {
val p = for {
a <- MyEither(0)
v <- MyEither.possibleError(12 / 0)
b <- MyEither.possibleError(32 / 12).handleError(_ => 1)
c <- MyEither(2)
} yield a + b + c + v
assert(p.isError)
}
}
}
}
3 changes: 2 additions & 1 deletion index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ layout: default
* [Задание 1](homeworks/homework_1/homework_1.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_1)
* [Задание 2](homeworks/homework_2/homework_2.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_2)
* [Задание 3](homeworks/homework_3/homework_3.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_3)
* [Задание 4](homeworks/homework_4/homework_4.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_4)
* [Задание 4](homeworks/homework_4/homework_4.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_4)
* [Задание 5](homeworks/homework_5/homework_5.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2023.autumn/tree/master/homeworks/homework_5)

0 comments on commit e7c65d4

Please sign in to comment.