-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0111c3f
commit e7c65d4
Showing
10 changed files
with
341 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = ??? | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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))) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters