Skip to content

Commit

Permalink
Добавил инфраструктуру для HW5
Browse files Browse the repository at this point in the history
  • Loading branch information
butyanov committed Jul 5, 2023
1 parent 6680db9 commit 1f87ba9
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 1 deletion.
9 changes: 9 additions & 0 deletions DotNetHomework.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Homework4", "Homework4", "{
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Hw4", "Homework4\Hw4\Hw4.fsproj", "{D658CAA8-AADE-42B6-9DFB-F5E950428D6D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Homework5", "Homework5", "{AB48D6BE-1E15-4042-A170-3A93D923C474}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Hw5", "Homework5\Hw5\Hw5.fsproj", "{4F247AA8-1E48-4EF9-9737-B4EAC3198408}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -71,6 +75,10 @@ Global
{D658CAA8-AADE-42B6-9DFB-F5E950428D6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D658CAA8-AADE-42B6-9DFB-F5E950428D6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D658CAA8-AADE-42B6-9DFB-F5E950428D6D}.Release|Any CPU.Build.0 = Release|Any CPU
{4F247AA8-1E48-4EF9-9737-B4EAC3198408}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F247AA8-1E48-4EF9-9737-B4EAC3198408}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F247AA8-1E48-4EF9-9737-B4EAC3198408}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F247AA8-1E48-4EF9-9737-B4EAC3198408}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{82C31766-E0A2-4367-A1B9-CDF58076C48B} = {1A7E5397-CFA9-4D54-A41D-DB294B9706D1}
Expand All @@ -80,5 +88,6 @@ Global
{13B96CAA-8E12-4415-839F-631F5381B3D8} = {3BCA2CBD-F82F-4593-B66B-D1818FE75B0D}
{89E62EA9-9073-4247-B7D8-FEAD45F7BDAB} = {1A7E5397-CFA9-4D54-A41D-DB294B9706D1}
{D658CAA8-AADE-42B6-9DFB-F5E950428D6D} = {ECE33078-B96D-4B47-B87C-445420244179}
{4F247AA8-1E48-4EF9-9737-B4EAC3198408} = {AB48D6BE-1E15-4042-A170-3A93D923C474}
EndGlobalSection
EndGlobal
25 changes: 25 additions & 0 deletions Homework5/Hw5/Calculator.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Hw5.Calculator

open System

type CalculatorOperation =
| Plus = 0
| Minus = 1
| Multiply = 2
| Divide = 3

[<Literal>]
let plus = "+"

[<Literal>]
let minus = "-"

[<Literal>]
let multiply = "*"

[<Literal>]
let divide = "/"

[<System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage>]
let inline calculate value1 operation value2: 'a =
(NotImplementedException() |> raise)
16 changes: 16 additions & 0 deletions Homework5/Hw5/Hw5.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="MaybeBuilder.fs" />
<Compile Include="Message.fs" />
<Compile Include="Calculator.fs" />
<Compile Include="Parser.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

</Project>
10 changes: 10 additions & 0 deletions Homework5/Hw5/MaybeBuilder.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Hw5.MaybeBuilder

open System

type MaybeBuilder() =
member builder.Bind(a, f): Result<'e,'d> =
(NotImplementedException() |> raise)
member builder.Return x: Result<'a,'b> =
(NotImplementedException() |> raise)
let maybe = MaybeBuilder()
8 changes: 8 additions & 0 deletions Homework5/Hw5/Message.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Hw5

type Message =
| SuccessfulExecution = 0
| WrongArgLength = 1
| WrongArgFormat = 2
| WrongArgFormatOperation = 3
| DivideByZero = 4
21 changes: 21 additions & 0 deletions Homework5/Hw5/Parser.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Hw5.Parser

open System
open Hw5.Calculator

let isArgLengthSupported (args:string[]): Result<'a,'b> =
(NotImplementedException() |> raise)

[<System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage>]
let inline isOperationSupported (arg1, operation, arg2): Result<('a * CalculatorOperation * 'b), Message> =
(NotImplementedException() |> raise)

let parseArgs (args: string[]): Result<('a * CalculatorOperation * 'b), Message> =
(NotImplementedException() |> raise)

[<System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage>]
let inline isDividingByZero (arg1, operation, arg2): Result<('a * CalculatorOperation * 'b), Message> =
(NotImplementedException() |> raise)

let parseCalcArguments (args: string[]): Result<'a, 'b> =
(NotImplementedException() |> raise)
3 changes: 3 additions & 0 deletions Homework5/Hw5/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
open System

(NotImplementedException() |> raise)
27 changes: 27 additions & 0 deletions Homework5/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Домашняя работа №5

## Монадки

*В Java-команду приходит программист и предлагает переписать все на Scala. Команда посовещавшись
отвечает: “забирай свои монадки и проваливай отсюда…”*

### Теория
1. [Материалы лекции](https://docs.google.com/presentation/d/1wNLLm4mgdve8BnMIxaEajKHLV401-l_8PR0vg91UBqs/edit#slide=id.p100)
2. [Сила композиции](https://habr.com/ru/company/jugru/blog/553028/) (монады)
3. [Функторы, аппликативные функторы и монады в картинках, без картинок](https://habr.com/ru/post/183150/)
4. [Understanding map and apply, Understanding bind, Using the core functions in practice](https://fsharpforfunandprofit.com/posts/elevated-world/)
5. [Composition with an Either computation expression](https://blog.ploeh.dk/2016/03/21/composition-with-an-either-computation-expression/)
6. [Железнодорожно-ориентированное программирование](https://habr.com/ru/post/339606/) ([расширенная версия с >=>](https://fsharpforfunandprofit.com/posts/recipe-part2/))

### Вопросы к семинару
1. Выберите одно слово, заканчивающиеся на *-able* или *-емый/емая*, чтобы описать сущность монад
2. Что общего у монадических типов Option, Seq, Task? Чем они отличаются?
3. С помощью какой функции может быть выражена функциональная композиция для монадических типов?
4. Как с помощью bind и return получить map?
5. Как с помощью map и return получить bind?
6. Как связаны побочные эффекты, ссылочная прозрачность и чистота функций? Почему в Haskell функции по-умолчанию чистые?
7. Как сделать ранний возврат из функции в F# без использования исключений?

### Практика
1. Использовать [Result Computation Expression](https://fsharpforfunandprofit.com/posts/computation-expressions-intro/) для обработки ошибок калькулятора на F#. Программа не должна выбрасывать исключения.
2. Добавить тесты на операции с типами int, float, double, decimal). Допустимо использовать несколько билдеров, inline-функций, дженерики вида ’ или ^ и другие возможности F#. Предпочтение следует отдать решениям с минимальным дублированием кода.
59 changes: 59 additions & 0 deletions Tests.FSharp/Homework5/CalculatorTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module Tests.Homework5.CalculatorTests

open Hw5.Calculator
open Microsoft.FSharp.Core
open Tests.RunLogic.Attributes
open Xunit

let epsilon: decimal = 0.001m

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData(15, 5, CalculatorOperation.Plus, 20)>]
[<InlineData(15, 5, CalculatorOperation.Minus, 10)>]
[<InlineData(15, 5, CalculatorOperation.Multiply, 75)>]
[<InlineData(15, 5, CalculatorOperation.Divide, 3)>]
let ``+, -, *, / work return correct calculation results with ints`` (value1 : int, value2: int, operation, expectedValue : int) =
//act
let actual = calculate value1 operation value2

//assert
Assert.Equal(expectedValue, actual)

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Plus, 21.2)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Minus, 10)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Multiply, 87.36)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Divide, 2.7857)>]
let ``+, -, *, / work return correct calculation results with floats``
(value1 : float, value2: float, operation, expectedValue : float) =
//act
let actual = abs (expectedValue - calculate value1 operation value2)

//assert
Assert.True(actual |> decimal < epsilon)

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Plus, 21.2)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Minus, 10)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Multiply, 87.36)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Divide, 2.7857)>]
let ``+, -, *, / work return correct calculation results with doubles``
(value1 : double, value2: double, operation, expectedValue : double) =
//act
let actual = abs (expectedValue - calculate value1 operation value2)

//assert
Assert.True(actual |> decimal < epsilon)

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Plus, 21.2)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Minus, 10)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Multiply, 87.36)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Divide, 2.7857)>]
let ``+, -, *, / work return correct calculation results with decimals``
(value1 : decimal, value2: decimal, operation, expectedValue : decimal) =
//act
let actual = abs (expectedValue - calculate value1 operation value2)

//assert
Assert.True(actual < epsilon)
138 changes: 138 additions & 0 deletions Tests.FSharp/Homework5/ParserTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
module Tests.Homework5.ParserTests

open System
open Hw5
open Hw5.Calculator
open Hw5.Parser
open Microsoft.FSharp.Core
open Tests.RunLogic.Attributes
open Xunit

let epsilon: decimal = 0.001m

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData(15, 5, CalculatorOperation.Plus, 20)>]
[<InlineData(15, 5, CalculatorOperation.Minus, 10)>]
[<InlineData(15, 5, CalculatorOperation.Multiply, 75)>]
[<InlineData(15, 5, CalculatorOperation.Divide, 3)>]
let ``ints parsed correctly`` (value1 : int, value2: int, operation, expectedValue : int) =
//act
let actual = Calculator.calculate value1 operation value2

//assert
Assert.Equal(expectedValue, actual)

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Plus, 21.2)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Minus, 10)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Multiply, 87.36)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Divide, 2.7857)>]
let ``floats parsed correctly`` (value1 : float, value2: float, operation, expectedValue : float) =
//act
let actual = (abs (expectedValue - Calculator.calculate value1 operation value2))

//assert
Assert.True(actual |> decimal < epsilon)

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Plus, 21.2)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Minus, 10)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Multiply, 87.36)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Divide, 2.7857)>]
let ``doubles parsed correctly`` (value1 : double, value2: double, operation, expectedValue : double) =
//act
let actual = (abs (expectedValue - Calculator.calculate value1 operation value2))

//assert
Assert.True(actual |> decimal < epsilon)

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Plus, 21.2)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Minus, 10)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Multiply, 87.36)>]
[<InlineData(15.6, 5.6, CalculatorOperation.Divide, 2.7857)>]
let ``decimals parsed correctly`` (value1 : decimal, value2: decimal, operation, expectedValue : decimal) =
//act
let actual = (abs (expectedValue - Calculator.calculate value1 operation value2))

//assert
Assert.True(actual < epsilon)

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData("15", "+", "5", 20)>]
[<InlineData("15", "-", "5", 10)>]
[<InlineData("15", "*", "5", 75)>]
[<InlineData("15", "/", "5", 3)>]
[<InlineData("15.6", "+", "5.6", 21.2)>]
[<InlineData("15.6", "-", "5.6", 10)>]
[<InlineData("15.6", "*", "5.6", 87.36)>]
[<InlineData("15.6", "/", "5.6", 2.7857)>]
let ``values parsed correctly`` (value1, operation, value2, expectedValue) =
//arrange
let values = [|value1;operation;value2|]

//act
let result = parseCalcArguments values

//assert
match result with
| Ok resultOk ->
match resultOk with
| arg1, operation, arg2 -> Assert.True((abs (expectedValue - Calculator.calculate arg1 operation arg2)) |> decimal < epsilon)
| Error e -> raise (InvalidOperationException(e))

[<HomeworkTheory(Homeworks.HomeWork5)>]
[<InlineData("f", "+", "3")>]
[<InlineData("3", "+", "f")>]
[<InlineData("a", "+", "f")>]
let ``Incorrect values return Error`` (value1, operation, value2) =
//arrange
let args = [|value1;operation;value2|]

//act
let result = parseCalcArguments args

//assert
match result with
| Ok _ -> raise (InvalidOperationException("This test must always return Error Result Type"))
| Error resultError -> Assert.Equal(resultError, Message.WrongArgFormat)

[<Homework(Homeworks.HomeWork5)>]
let ``Incorrect operations return Error`` () =
//arrange
let args = [|"3";".";"4"|]

//act
let result = parseCalcArguments args

//assert
match result with
| Ok _ -> raise (InvalidOperationException("This test must always return Error Result Type"))
| Error resultError -> Assert.Equal(resultError, Message.WrongArgFormatOperation)

[<Homework(Homeworks.HomeWork5)>]
let ``Incorrect argument count throws ArgumentException`` () =
//arrange
let args = [|"3";"+";"4";"5"|]

//act
let result = parseCalcArguments args

//assert
match result with
| Ok _ -> raise (InvalidOperationException("This test must always return Error Result Type"))
| Error resultError -> Assert.Equal(resultError, Message.WrongArgLength)

[<Homework(Homeworks.HomeWork5)>]
let ``any / 0 -> Error(Message.DivideByZero)`` () =
//arrange
let args = [|"3";"/";"0"|]

//act
let result = parseCalcArguments args

//assert
match result with
| Ok _ -> raise (InvalidOperationException("This test must always return Error Result Type"))
| Error resultError -> Assert.Equal(resultError, Message.DivideByZero)

4 changes: 3 additions & 1 deletion Tests.FSharp/Tests.FSharp.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>

<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="Homework4\CalculatorTests.fs" />
<Compile Include="Homework4\ParserTests.fs" />
<Compile Include="Homework5\CalculatorTests.fs" />
<Compile Include="Homework5\ParserTests.fs" />
</ItemGroup>

<ItemGroup>
Expand All @@ -27,6 +28,7 @@

<ItemGroup>
<ProjectReference Include="..\Homework4\Hw4\Hw4.fsproj" />
<ProjectReference Include="..\Homework5\Hw5\Hw5.fsproj" />
<ProjectReference Include="..\Tests.RunLogic\Tests.RunLogic.csproj" />
</ItemGroup>

Expand Down

0 comments on commit 1f87ba9

Please sign in to comment.