From 6926215e505bc5f36eb138cbec07a3c36c1dc1f1 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:22:37 +0300 Subject: [PATCH 1/3] Map, Filter, Fold (hw7 - funcs) --- .../Functions.Tests/Functions.Tests.csproj | 28 +++++ Functions/Functions.sln | 28 +++++ Functions/Functions/Functions.cs | 110 ++++++++++++++++++ Functions/Functions/Functions.csproj | 10 ++ 4 files changed, 176 insertions(+) create mode 100644 Functions/Functions.Tests/Functions.Tests.csproj create mode 100644 Functions/Functions.sln create mode 100644 Functions/Functions/Functions.cs create mode 100644 Functions/Functions/Functions.csproj diff --git a/Functions/Functions.Tests/Functions.Tests.csproj b/Functions/Functions.Tests/Functions.Tests.csproj new file mode 100644 index 0000000..12e42e8 --- /dev/null +++ b/Functions/Functions.Tests/Functions.Tests.csproj @@ -0,0 +1,28 @@ + + + + net9.0 + latest + enable + enable + + true + + + + + + + + + + + + + + + + + + + diff --git a/Functions/Functions.sln b/Functions/Functions.sln new file mode 100644 index 0000000..a19ec08 --- /dev/null +++ b/Functions/Functions.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Functions", "Functions\Functions.csproj", "{E71A4E78-9D6C-4616-9F99-3B067B9B92F3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Functions.Tests", "Functions.Tests\Functions.Tests.csproj", "{D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E71A4E78-9D6C-4616-9F99-3B067B9B92F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E71A4E78-9D6C-4616-9F99-3B067B9B92F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E71A4E78-9D6C-4616-9F99-3B067B9B92F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E71A4E78-9D6C-4616-9F99-3B067B9B92F3}.Release|Any CPU.Build.0 = Release|Any CPU + {D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Functions/Functions/Functions.cs b/Functions/Functions/Functions.cs new file mode 100644 index 0000000..31d6bc6 --- /dev/null +++ b/Functions/Functions/Functions.cs @@ -0,0 +1,110 @@ +namespace Functions; + +/// +/// Utility class that contains Map, Filter and Fold functions. +/// +public static class Functions +{ + /// + /// Maps all elements from to elements of type using function. + /// + /// Source type. + /// Type to map to. + /// Source to map. + /// Function that maps to . + /// Sequence of mapped elements. + public static IEnumerable Map(this IEnumerable source, Func map) + { + foreach (var item in source) + { + yield return map(item); + } + } + + /// + /// Filters elements from that satisfy . + /// + /// Type of elements. + /// Source to filter. + /// Function that filters elements; if it returns , adds element to the result, otherwise, doesn't. + /// Sequence of filtered elements. + public static IEnumerable Filter(this IEnumerable source, Func predicate) + { + foreach (var item in source) + { + if (predicate(item)) + { + yield return item; + } + } + } + + /// + /// Folds all elements in into one element. + /// + /// Type of elements. + /// Source to fold. + /// Function that is used to accumulate result. + /// Accumulated value. + /// Sequence is empty. + public static T Fold(this IEnumerable source, Func folder) + { + var enumerator = source.GetEnumerator(); + + if (!enumerator.MoveNext()) + { + throw new InvalidOperationException("No elements in source"); + } + + var value = enumerator.Current; + + while (enumerator.MoveNext()) + { + value = folder(value, enumerator.Current); + } + + return value; + } + + /// + /// Folds all elements in into one element. + /// + /// Type of elements in . + /// The type of accumulated value. + /// Source to fold. + /// Initial value of accumulated value. + /// Function that is used to accumulate result. + /// Accumulated value. + public static TResult Fold(this IEnumerable source, TResult initialValue, Func folder) + { + var value = initialValue; + foreach (var item in source) + { + value = folder(value, item); + } + + return value; + } + + /// + /// Folds all elements in into one element. + /// + /// Type of elements in . + /// The type of accumulated value. + /// The result type. + /// Source to fold. + /// Initial value of accumulated value. + /// Function that is used to accumulate result. + /// Function that is used to convert accumulated result. + /// Converted accumulated value. + public static TResult Fold(this IEnumerable source, TIntermediate initialValue, Func folder, Func map) + { + var value = initialValue; + foreach (var item in source) + { + value = folder(value, item); + } + + return map(value); + } +} diff --git a/Functions/Functions/Functions.csproj b/Functions/Functions/Functions.csproj new file mode 100644 index 0000000..bf05b58 --- /dev/null +++ b/Functions/Functions/Functions.csproj @@ -0,0 +1,10 @@ + + + + Library + net9.0 + enable + enable + + + From 3a900c4a471474b162b179ee0718f27d7995d87d Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Fri, 30 May 2025 14:09:08 +0300 Subject: [PATCH 2/3] Added license header (hw7 - funcs) --- Functions/Functions/Functions.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Functions/Functions/Functions.cs b/Functions/Functions/Functions.cs index 31d6bc6..a640461 100644 --- a/Functions/Functions/Functions.cs +++ b/Functions/Functions/Functions.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + namespace Functions; /// From 8fb3933c593e1c223adc6b2201b99c384f0b3384 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Fri, 30 May 2025 14:12:21 +0300 Subject: [PATCH 3/3] Added tests (hw7 - funcs) --- Functions/Functions.Tests/FunctionsTests.cs | 94 +++++++++++++++++++ .../Functions.Tests/GlobalSuppressions.cs | 11 +++ 2 files changed, 105 insertions(+) create mode 100644 Functions/Functions.Tests/FunctionsTests.cs create mode 100644 Functions/Functions.Tests/GlobalSuppressions.cs diff --git a/Functions/Functions.Tests/FunctionsTests.cs b/Functions/Functions.Tests/FunctionsTests.cs new file mode 100644 index 0000000..134f2e6 --- /dev/null +++ b/Functions/Functions.Tests/FunctionsTests.cs @@ -0,0 +1,94 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + +namespace Functions.Tests; + +public class FunctionsTests +{ + private static readonly IEnumerable NumberSequence = Enumerable.Range(0, 120); + + [Test] + public void Map_ConvertsNumbersToString_SameAs_Select() + { + AssertMapBehavesSameAsSelect(NumberSequence, x => x.ToString()); + } + + [Test] + public void Map_ConvertsStringToNumbers_SameAs_Select() + { + AssertMapBehavesSameAsSelect(NumberSequence.Select(x => x.ToString()), int.Parse); + } + + [Test] + public void Filter_SelectsNumbers_SameAs_Where() + { + AssertFilerBehavesSameAsWhere(NumberSequence, x => x % 2 == 0); + } + + [Test] + public void Filter_SelectsStrings_SameAs_Where() + { + AssertFilerBehavesSameAsWhere(NumberSequence.Select(x => x.ToString()), x => x.Contains('4')); + } + + [Test] + public void Fold_SumsNumbers_SameAsAggregate() + { + static int Sum(int x, int y) => x + y; + + var aggregateResult = NumberSequence.Aggregate(Sum); + var foldResult = NumberSequence.Fold(Sum); + + Assert.That(aggregateResult, Is.EqualTo(foldResult)); + } + + [Test] + public void Fold_AddsCharacters_SameAsAggregate() + { + static string Sum(string x, char y) => x + y; + + var source = Enumerable.Range('0', '~' - '0').Select(x => (char)x); + + var aggregateResult = source.Aggregate(string.Empty, Sum); + var foldResult = source.Fold(string.Empty, Sum); + + Assert.That(aggregateResult, Is.EqualTo(foldResult)); + } + + [Test] + public void Fold_AddsCharactersAndReverses_SameAsAggregate() + { + static string Sum(string x, char y) => x + y; + static string Reverse(string s) => string.Concat(s.Reverse()); + + var source = Enumerable.Range('0', '~' - '0').Select(x => (char)x); + + var aggregateResult = source.Aggregate(string.Empty, Sum, Reverse); + var foldResult = source.Fold(string.Empty, Sum, Reverse); + + Assert.That(aggregateResult, Is.EqualTo(foldResult)); + } + + [Test] + public void Fold_Throws_OnEmptyCollection() + { + Assert.Throws(() => Enumerable.Range(0, 0).Fold((x, y) => x + y)); + } + + private static void AssertMapBehavesSameAsSelect(IEnumerable source, Func map) + { + var selectResult = source.Select(map); + var mapResult = source.Map(map); + + Assert.That(selectResult.SequenceEqual(mapResult), Is.True); + } + + private static void AssertFilerBehavesSameAsWhere(IEnumerable source, Func filter) + { + var whereResult = source.Where(filter); + var filterResult = source.Filter(filter); + + Assert.That(whereResult.SequenceEqual(filterResult), Is.True); + } +} diff --git a/Functions/Functions.Tests/GlobalSuppressions.cs b/Functions/Functions.Tests/GlobalSuppressions.cs new file mode 100644 index 0000000..909e036 --- /dev/null +++ b/Functions/Functions.Tests/GlobalSuppressions.cs @@ -0,0 +1,11 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "This is tests project")]