From f3dcb45200793bd41ea2976963d7863ced93877d Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Sun, 17 Aug 2025 18:25:18 +0100 Subject: [PATCH 01/18] Add personal ignore file --- Mark-CSharp/.gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Mark-CSharp/.gitignore diff --git a/Mark-CSharp/.gitignore b/Mark-CSharp/.gitignore new file mode 100644 index 0000000..1c97ff6 --- /dev/null +++ b/Mark-CSharp/.gitignore @@ -0,0 +1,8 @@ +#Ignore any test result output from unit testing +/[T][t]est[R][r]esults/ + +# Ignore .idea folder +/.idea/ + +# Ignore any personal settings +*.[D][d]ot[S][s]ettings.* From a47d3aa88e07a174cdf4ce109b57548680229d2e Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Sun, 17 Aug 2025 18:29:19 +0100 Subject: [PATCH 02/18] Inital commit for console app --- Mark-CSharp/WhyFunctional.sln | 34 +++++++++++++++++++ Mark-CSharp/WhyFunctional/Program.cs | 9 +++++ .../WhyFunctional/WhyFunctional.csproj | 10 ++++++ 3 files changed, 53 insertions(+) create mode 100644 Mark-CSharp/WhyFunctional.sln create mode 100644 Mark-CSharp/WhyFunctional/Program.cs create mode 100644 Mark-CSharp/WhyFunctional/WhyFunctional.csproj diff --git a/Mark-CSharp/WhyFunctional.sln b/Mark-CSharp/WhyFunctional.sln new file mode 100644 index 0000000..d481dff --- /dev/null +++ b/Mark-CSharp/WhyFunctional.sln @@ -0,0 +1,34 @@ + +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}") = "WhyFunctional", "WhyFunctional\WhyFunctional.csproj", "{8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Debug|x64.ActiveCfg = Debug|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Debug|x64.Build.0 = Debug|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Debug|x86.ActiveCfg = Debug|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Debug|x86.Build.0 = Debug|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|Any CPU.Build.0 = Release|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|x64.ActiveCfg = Release|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|x64.Build.0 = Release|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|x86.ActiveCfg = Release|Any CPU + {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Mark-CSharp/WhyFunctional/Program.cs b/Mark-CSharp/WhyFunctional/Program.cs new file mode 100644 index 0000000..ba5ab49 --- /dev/null +++ b/Mark-CSharp/WhyFunctional/Program.cs @@ -0,0 +1,9 @@ +namespace WhyFunctional; + +internal static class Program +{ + public static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + } +} diff --git a/Mark-CSharp/WhyFunctional/WhyFunctional.csproj b/Mark-CSharp/WhyFunctional/WhyFunctional.csproj new file mode 100644 index 0000000..206b89a --- /dev/null +++ b/Mark-CSharp/WhyFunctional/WhyFunctional.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + From e9b69c8e6364fcfd7180d8a7226cf53b5a4a6333 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Sun, 17 Aug 2025 23:54:17 +0100 Subject: [PATCH 03/18] Initial commit for ListOf class implementation --- Mark-CSharp/WhyFunctional/ListOf.cs | 93 +++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Mark-CSharp/WhyFunctional/ListOf.cs diff --git a/Mark-CSharp/WhyFunctional/ListOf.cs b/Mark-CSharp/WhyFunctional/ListOf.cs new file mode 100644 index 0000000..c1dbe3a --- /dev/null +++ b/Mark-CSharp/WhyFunctional/ListOf.cs @@ -0,0 +1,93 @@ +using System.Numerics; + +namespace WhyFunctional; + +public class ListOf + where T : INumber +{ + #region Fields + + /// + /// Holds the first value for the object + /// + private readonly T _value = default(T)!; + + /// + /// Holds all other values for the object + /// + private readonly ListOf _others = null!; + + #endregion + + #region Properties + + /// + /// Helper property to indicate whether the value of the object is Nil + /// + public bool IsNil { get; } + + /// + /// Helper property converts all values into an array + /// + public T[] ToArray + { + get + { + return IsNil + ? Array.Empty() + : new T[] { _value }.Concat(_others.ToArray).ToArray(); + } + } + + #endregion + + #region Constructors + + /// + /// Default ctor - creates an object equivalent to + /// + public ListOf() + { + IsNil = true; + } + + /// + /// Alternate ctor - must supply both values as non-null + /// + /// The first value + /// The other values + public ListOf(T value, ListOf others) + { + ArgumentNullException.ThrowIfNull(value, nameof(value)); + ArgumentNullException.ThrowIfNull(others, nameof(others)); + _value = value; + _others = others; + IsNil = false; + } + + #endregion + + #region Methods + + /// + /// Static method returns the equivalent of an object with an empty array + /// + /// The newly created empty object + public static ListOf Nil() + { + return new ListOf(); + } + + /// + /// Static "constructor" to initialise the object with two non-null values + /// + /// The first value for the object + /// All other values + /// The newly created object containing the specified values + public static ListOf Cons(T value, ListOf others) + { + return new ListOf(value, others); + } + + #endregion +} \ No newline at end of file From 811e3b3b2d312ba03cd311e748d8a97c78aba4d0 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Mon, 18 Aug 2025 00:06:55 +0100 Subject: [PATCH 04/18] Tests to verify Exercise 1 --- .../WhyFunctional.Tests/ListOfTests.cs | 43 +++++++++++++++++++ .../WhyFunctional.Tests/MSTestSettings.cs | 1 + .../WhyFunctional.Tests.csproj | 24 +++++++++++ Mark-CSharp/WhyFunctional.sln | 14 ++++++ 4 files changed, 82 insertions(+) create mode 100644 Mark-CSharp/WhyFunctional.Tests/ListOfTests.cs create mode 100644 Mark-CSharp/WhyFunctional.Tests/MSTestSettings.cs create mode 100644 Mark-CSharp/WhyFunctional.Tests/WhyFunctional.Tests.csproj diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfTests.cs new file mode 100644 index 0000000..fc3a551 --- /dev/null +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfTests.cs @@ -0,0 +1,43 @@ +using FluentAssertions; + +namespace WhyFunctional.Tests; + +[TestClass] +public sealed class ListOfTests +{ + [TestMethod] + public void VerifyNilProducesEmptyResult() + { + ListOf sut = ListOf.Nil(); + + sut.Should().NotBeNull(); + sut.ToArray.Should().BeEmpty(); + sut.IsNil.Should().BeTrue(); + } + + [TestMethod] + public void VerifySingleElement() + { + ListOf sut = ListOf.Cons(1, ListOf.Nil()); + + sut.Should().NotBeNull(); + int[] values = sut.ToArray; + values.Should().NotBeEmpty(); + values.Should().HaveCount(1); + values.Should().Contain(1); + sut.IsNil.Should().BeFalse(); + } + + [TestMethod] + public void VerifyThreeElements() + { + ListOf sut = ListOf.Cons(1, ListOf.Cons(2, ListOf.Cons(3, ListOf.Nil()))); + + sut.Should().NotBeNull(); + int[] values = sut.ToArray; + values.Should().NotBeEmpty(); + values.Should().HaveCount(3); + values.Should().Contain([1, 2, 3]); + sut.IsNil.Should().BeFalse(); + } +} \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional.Tests/MSTestSettings.cs b/Mark-CSharp/WhyFunctional.Tests/MSTestSettings.cs new file mode 100644 index 0000000..aaf278c --- /dev/null +++ b/Mark-CSharp/WhyFunctional.Tests/MSTestSettings.cs @@ -0,0 +1 @@ +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/Mark-CSharp/WhyFunctional.Tests/WhyFunctional.Tests.csproj b/Mark-CSharp/WhyFunctional.Tests/WhyFunctional.Tests.csproj new file mode 100644 index 0000000..2585ecb --- /dev/null +++ b/Mark-CSharp/WhyFunctional.Tests/WhyFunctional.Tests.csproj @@ -0,0 +1,24 @@ + + + + net8.0 + latest + enable + enable + + + + + + + + + + + + + + + + + diff --git a/Mark-CSharp/WhyFunctional.sln b/Mark-CSharp/WhyFunctional.sln index d481dff..fb0701b 100644 --- a/Mark-CSharp/WhyFunctional.sln +++ b/Mark-CSharp/WhyFunctional.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhyFunctional", "WhyFunctional\WhyFunctional.csproj", "{8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhyFunctional.Tests", "WhyFunctional.Tests\WhyFunctional.Tests.csproj", "{BCDE153E-CE0B-461C-AC74-35B230F3DE56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,18 @@ Global {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|x64.Build.0 = Release|Any CPU {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|x86.ActiveCfg = Release|Any CPU {8F6902EC-8687-4F79-AA2F-F3A1CF7191F4}.Release|x86.Build.0 = Release|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Debug|x64.ActiveCfg = Debug|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Debug|x64.Build.0 = Debug|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Debug|x86.ActiveCfg = Debug|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Debug|x86.Build.0 = Debug|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Release|Any CPU.Build.0 = Release|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Release|x64.ActiveCfg = Release|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Release|x64.Build.0 = Release|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Release|x86.ActiveCfg = Release|Any CPU + {BCDE153E-CE0B-461C-AC74-35B230F3DE56}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From c74e265e1dcd69fde37ea2171f5a7cd5b0285551 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Mon, 18 Aug 2025 01:37:05 +0100 Subject: [PATCH 05/18] Make constructors private: instances shall be created using the Nil and Cons methods --- Mark-CSharp/WhyFunctional/ListOf.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mark-CSharp/WhyFunctional/ListOf.cs b/Mark-CSharp/WhyFunctional/ListOf.cs index c1dbe3a..b67a92c 100644 --- a/Mark-CSharp/WhyFunctional/ListOf.cs +++ b/Mark-CSharp/WhyFunctional/ListOf.cs @@ -46,7 +46,7 @@ public T[] ToArray /// /// Default ctor - creates an object equivalent to /// - public ListOf() + private ListOf() { IsNil = true; } @@ -56,7 +56,7 @@ public ListOf() /// /// The first value /// The other values - public ListOf(T value, ListOf others) + private ListOf(T value, ListOf others) { ArgumentNullException.ThrowIfNull(value, nameof(value)); ArgumentNullException.ThrowIfNull(others, nameof(others)); From 5982dea63cb29955f4a5befdcc9f7f82510b49d1 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Mon, 18 Aug 2025 17:12:14 +0100 Subject: [PATCH 06/18] Updated to match requirements for Exercise 2 --- .../WhyFunctional.Tests/ListOfTests.cs | 52 +++++++++++++++++++ Mark-CSharp/WhyFunctional/ListOf.cs | 10 ++++ 2 files changed, 62 insertions(+) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfTests.cs index fc3a551..5076487 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfTests.cs @@ -40,4 +40,56 @@ public void VerifyThreeElements() values.Should().Contain([1, 2, 3]); sut.IsNil.Should().BeFalse(); } + + [TestMethod] + [DataRow(new int[] { 1 }, 1)] + [DataRow(new int[] { 13, 27, 3 }, 13)] + public void VerifyHeadProperty(int[] values, int expected) + { + ListOf sut = ListOf.Nil(); + foreach (int value in values.Reverse()) + { + sut = ListOf.Cons(value, sut); + } + + sut.Should().NotBeNull(); + int[] sutValues = sut.ToArray; + sutValues.Should().HaveCount(values.Length); + sutValues.Should().Contain(expected); + sut.IsNil.Should().BeFalse(); + sut.Head.Should().Be(expected); + } + + public static IEnumerable VerifyTailTestData() + { + var tv_1 = ListOf.Cons(1, ListOf.Nil()); + var tv_3 = ListOf.Cons(3, ListOf.Nil()); + var tv_27 = ListOf.Cons(27, tv_3); + var tv_13 = ListOf.Cons(13, tv_27); + return + [ + [tv_1.ToArray, tv_1.Head, Array.Empty()], + [tv_13.ToArray, tv_13.Head, tv_27.ToArray] + ]; + } + + [TestMethod] + [DynamicData(nameof(VerifyTailTestData), DynamicDataSourceType.Method)] + public void VerifyTailProperty(int[] values, int expectedHead, int[] expectedTail) + { + ListOf sut = ListOf.Nil(); + foreach (int value in values.Reverse()) + { + sut = ListOf.Cons(value, sut); + } + + sut.Should().NotBeNull(); + int[] sutValues = sut.ToArray; + sutValues.Should().HaveCount(values.Length); + sutValues.Should().Contain(expectedHead); + sut.IsNil.Should().BeFalse(); + sut.Head.Should().Be(expectedHead); + sut.Tail.ToArray.Should().HaveCount(expectedTail.Length); + sut.Tail.ToArray.Should().ContainInOrder(expectedTail); + } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOf.cs b/Mark-CSharp/WhyFunctional/ListOf.cs index b67a92c..4ce3893 100644 --- a/Mark-CSharp/WhyFunctional/ListOf.cs +++ b/Mark-CSharp/WhyFunctional/ListOf.cs @@ -39,6 +39,16 @@ public T[] ToArray } } + /// + /// Returns the value at the "head" of the + /// + public T Head => _value; + + /// + /// Returns the value of the other items in the + /// + public ListOf Tail => _others; + #endregion #region Constructors From 3dca4f3f60150bec202cbb29f6ef0b726ca7a295 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Tue, 19 Aug 2025 09:03:26 +0100 Subject: [PATCH 07/18] Updated to match requirements for Exercise 3 --- .../ListOfExtensionsTests.cs | 25 +++++++++++++++++++ Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 13 ++++++++++ 2 files changed, 38 insertions(+) create mode 100644 Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs create mode 100644 Mark-CSharp/WhyFunctional/ListOfExtensions.cs diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs new file mode 100644 index 0000000..22535e9 --- /dev/null +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -0,0 +1,25 @@ +using FluentAssertions; + +namespace WhyFunctional.Tests; + +[TestClass] +public class ListOfExtensionsTests +{ + [TestMethod] + [DataRow(new int[] { 1 })] + [DataRow(new int[] { 10 })] + [DataRow(new int[] { 1, 2, 3 })] + public void VerifySumExtension(int[] values) + { + int expected = 0; + ListOf sut = ListOf.Nil(); + foreach (int value in values.Reverse()) + { + sut = ListOf.Cons(value, sut); + expected += value; + } + + var result = sut.Sum(); + result.Should().Be(expected); + } +} \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs new file mode 100644 index 0000000..6c61b07 --- /dev/null +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -0,0 +1,13 @@ +using System.Numerics; + +namespace WhyFunctional; + +public static class ListOfExtensions +{ + public static T Sum(this ListOf list) where T : INumber + { + return list.IsNil + ? T.Zero + : list.Head + Sum(list.Tail); + } +} From cb8214f11f5ff045a32699db47112dafb6e33b59 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Tue, 19 Aug 2025 09:13:07 +0100 Subject: [PATCH 08/18] Updated to match requirements for Exercise 4 --- .../ListOfExtensionsTests.cs | 18 ++++++++++++++++ Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 21 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs index 22535e9..8e21e3b 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -22,4 +22,22 @@ public void VerifySumExtension(int[] values) var result = sut.Sum(); result.Should().Be(expected); } + + [TestMethod] + [DataRow(new int[] { 1 })] + [DataRow(new int[] { 10 })] + [DataRow(new int[] { 1, 2, 3 })] + public void VerifyProductExtension(int[] values) + { + int expected = 1; + ListOf sut = ListOf.Nil(); + foreach (int value in values.Reverse()) + { + sut = ListOf.Cons(value, sut); + expected *= value; + } + + var result = sut.Product(); + result.Should().Be(expected); + } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs index 6c61b07..e4b8208 100644 --- a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -4,10 +4,29 @@ namespace WhyFunctional; public static class ListOfExtensions { + /// + /// Returns the summation of the values contained within + /// + /// A values to be summed + /// + /// The summation result of values held in public static T Sum(this ListOf list) where T : INumber { return list.IsNil ? T.Zero : list.Head + Sum(list.Tail); } -} + + /// + /// Returns the product of the values contained in + /// + /// A values to be summed + /// + /// The product of values held in + public static T Product(this ListOf list) where T : INumber + { + return list.IsNil + ? T.One + : list.Head * Product(list.Tail); + } +} \ No newline at end of file From 17eaa887417871d7ef88b2b0d5c62ea305bbd954 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Tue, 19 Aug 2025 10:47:25 +0100 Subject: [PATCH 09/18] Updated to match requirements for Exercise 5 (FoldR) --- .../ListOfExtensionsTests.cs | 4 +- Mark-CSharp/WhyFunctional/ListOf.cs | 7 +++ Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 55 ++++++++++++++++--- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs index 8e21e3b..64db820 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -19,7 +19,7 @@ public void VerifySumExtension(int[] values) expected += value; } - var result = sut.Sum(); + var result = sut.Sum(); result.Should().Be(expected); } @@ -37,7 +37,7 @@ public void VerifyProductExtension(int[] values) expected *= value; } - var result = sut.Product(); + var result = sut.Product(); result.Should().Be(expected); } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOf.cs b/Mark-CSharp/WhyFunctional/ListOf.cs index 4ce3893..5ea1a68 100644 --- a/Mark-CSharp/WhyFunctional/ListOf.cs +++ b/Mark-CSharp/WhyFunctional/ListOf.cs @@ -100,4 +100,11 @@ public static ListOf Cons(T value, ListOf others) } #endregion + + public override string ToString() + { + return IsNil + ? "Nil" + : $"[{Head}, {Tail}]"; + } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs index e4b8208..327d6c7 100644 --- a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -9,12 +9,13 @@ public static class ListOfExtensions /// /// A values to be summed /// + /// /// The summation result of values held in - public static T Sum(this ListOf list) where T : INumber + public static TResult Sum(this ListOf list) + where T : notnull, INumber, IAdditiveIdentity, IAdditionOperators + where TResult : notnull, INumber { - return list.IsNil - ? T.Zero - : list.Head + Sum(list.Tail); + return list.FoldR(Add, TResult.Zero); } /// @@ -22,11 +23,51 @@ public static T Sum(this ListOf list) where T : INumber /// /// A values to be summed /// + /// /// The product of values held in - public static T Product(this ListOf list) where T : INumber + public static TResult Product(this ListOf list) + where T : notnull, INumber, IMultiplicativeIdentity, IMultiplyOperators + where TResult : notnull, INumber + { + return list.FoldR(Mul, TResult.One); + } + + /// + /// An addition function for use in operations + /// + /// The first value + /// The second value + /// + /// + /// The summation of and + private static TResult Add(T a, TResult b) + where T : notnull, INumber, IAdditiveIdentity, IAdditionOperators + where TResult : notnull, INumber + { + return a + b; + } + + /// + /// A multiplication function for use as a operation + /// + /// The first value + /// The second value + /// + /// + /// The product of and + private static TResult Mul(T a, TResult b) + where T : notnull, INumber, IMultiplicativeIdentity, IMultiplyOperators + where TResult : notnull, INumber + { + return a * b; + } + + public static TResult FoldR(this ListOf list, Func fn, TResult initial) + where T : notnull, INumber + where TResult : notnull { return list.IsNil - ? T.One - : list.Head * Product(list.Tail); + ? initial + : fn(list.Head, list.Tail.FoldR(fn, initial)); } } \ No newline at end of file From ea3520d88359017384c05419ab3ec6f1d29480e6 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Tue, 19 Aug 2025 11:04:41 +0100 Subject: [PATCH 10/18] Updated to match requirements for Exercise 5 (List Construction) --- .../ListOfExtensionsTests.cs | 13 +++++++++++ Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 23 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs index 64db820..9ef143a 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -40,4 +40,17 @@ public void VerifyProductExtension(int[] values) var result = sut.Product(); result.Should().Be(expected); } + + [TestMethod] + [DataRow(new int[] { })] + [DataRow(new int[] { 1 })] + [DataRow(new int[] { 1, 2, 3, 5, 7 })] + public void VerifyBuildListExtension(int[] values) + { + ListOf sut = values.BuildList(); + + sut.Should().NotBeNull(); + sut.ToArray.Length.Should().Be(values.Length); + sut.ToArray.Should().ContainInOrder(values); + } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs index 327d6c7..d723dd7 100644 --- a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -62,6 +62,15 @@ private static TResult Mul(T a, TResult b) return a * b; } + /// + /// Provides a Fold-Right function to perform the required operation () on the , starting with the initial value + /// + /// The to be manipulated + /// The function manipulating the + /// The initial value for the function + /// + /// + /// The result of applying over the public static TResult FoldR(this ListOf list, Func fn, TResult initial) where T : notnull, INumber where TResult : notnull @@ -70,4 +79,18 @@ public static TResult FoldR(this ListOf list, Func + /// Helper method to create a from an array of values + /// + /// The values to be added to the output + /// + /// A object, containing all values in + public static ListOf BuildList(this T[] values) + where T : notnull, INumber + { + return values.Length > 0 + ? ListOf.Cons(values[0], BuildList(values[1..])) + : ListOf.Nil(); + } } \ No newline at end of file From 7b20ed0f19c77dce7716f80a2f4322ac276404bd Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Wed, 20 Aug 2025 10:47:49 +0100 Subject: [PATCH 11/18] Refactoring for boolean logic operators --- Mark-CSharp/WhyFunctional/ListOf.cs | 2 +- Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 58 ++++++++++++++----- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/Mark-CSharp/WhyFunctional/ListOf.cs b/Mark-CSharp/WhyFunctional/ListOf.cs index 5ea1a68..3941857 100644 --- a/Mark-CSharp/WhyFunctional/ListOf.cs +++ b/Mark-CSharp/WhyFunctional/ListOf.cs @@ -3,7 +3,7 @@ namespace WhyFunctional; public class ListOf - where T : INumber + where T : notnull { #region Fields diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs index d723dd7..fccd86e 100644 --- a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -12,10 +12,10 @@ public static class ListOfExtensions /// /// The summation result of values held in public static TResult Sum(this ListOf list) - where T : notnull, INumber, IAdditiveIdentity, IAdditionOperators - where TResult : notnull, INumber + where T : INumber, IAdditiveIdentity, IAdditionOperators + where TResult : INumber { - return list.FoldR(Add, TResult.Zero); + return list.FoldR(Add, TResult.Zero); } /// @@ -26,10 +26,10 @@ public static TResult Sum(this ListOf list) /// /// The product of values held in public static TResult Product(this ListOf list) - where T : notnull, INumber, IMultiplicativeIdentity, IMultiplyOperators - where TResult : notnull, INumber + where T : INumber, IMultiplicativeIdentity, IMultiplyOperators + where TResult : INumber { - return list.FoldR(Mul, TResult.One); + return list.FoldR(Mul, TResult.One); } /// @@ -41,8 +41,8 @@ public static TResult Product(this ListOf list) /// /// The summation of and private static TResult Add(T a, TResult b) - where T : notnull, INumber, IAdditiveIdentity, IAdditionOperators - where TResult : notnull, INumber + where T : INumber, IAdditiveIdentity, IAdditionOperators + where TResult : INumber { return a + b; } @@ -56,8 +56,8 @@ private static TResult Add(T a, TResult b) /// /// The product of and private static TResult Mul(T a, TResult b) - where T : notnull, INumber, IMultiplicativeIdentity, IMultiplyOperators - where TResult : notnull, INumber + where T : INumber, IMultiplicativeIdentity, IMultiplyOperators + where TResult : INumber { return a * b; } @@ -71,8 +71,8 @@ private static TResult Mul(T a, TResult b) /// /// /// The result of applying over the - public static TResult FoldR(this ListOf list, Func fn, TResult initial) - where T : notnull, INumber + private static TResult FoldR(this ListOf list, Func fn, TResult initial) + where T : notnull where TResult : notnull { return list.IsNil @@ -87,10 +87,40 @@ public static TResult FoldR(this ListOf list, Func /// A object, containing all values in public static ListOf BuildList(this T[] values) - where T : notnull, INumber + where T : notnull { return values.Length > 0 - ? ListOf.Cons(values[0], BuildList(values[1..])) + ? ListOf.Cons(values[0], BuildList(values[1..])) : ListOf.Nil(); } + + /// + /// Provides a logical OR operation for two values + /// + /// The first value + /// The second value + /// The logical OR product + private static bool LogicalOr(bool a, bool b) => a || b; + + /// + /// Provides a logical AND operation for two values + /// + /// The first value + /// The second value + /// The logical AND product + private static bool LogicalAnd(bool a, bool b) => a && b; + + /// + /// Determines whether any of the are true + /// + /// A object with boolean values + /// True if any values are true, otherwise false + public static bool AnyTrue(ListOf values) => values.FoldR(LogicalOr, false); + + /// + /// Determines whether ALL of the are true + /// + /// A object with boolean values + /// True if ALL values are true, otherwise false + public static bool AllTrue(ListOf values) => values.FoldR(LogicalAnd, true); } \ No newline at end of file From 85c5c14b40b62578811506745918e0ec04b09a0f Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Wed, 20 Aug 2025 10:57:07 +0100 Subject: [PATCH 12/18] Add tests for boolean logic in Exercise 6 --- .../ListOfExtensionsTests.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs index 9ef143a..eb29a4d 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -19,10 +19,10 @@ public void VerifySumExtension(int[] values) expected += value; } - var result = sut.Sum(); + var result = sut.Sum(); result.Should().Be(expected); } - + [TestMethod] [DataRow(new int[] { 1 })] [DataRow(new int[] { 10 })] @@ -53,4 +53,37 @@ public void VerifyBuildListExtension(int[] values) sut.ToArray.Length.Should().Be(values.Length); sut.ToArray.Should().ContainInOrder(values); } + + [TestMethod] + [DataRow(new bool[] { }, false)] + [DataRow(new bool[] { false }, false)] + [DataRow(new bool[] { false, false, true }, true)] + [DataRow(new bool[] { false, true, false }, true)] + public void VerifyAnyTrueExtension(bool[] values, bool expected) + { + ListOf sut = values.BuildList(); + + sut.Should().NotBeNull(); + sut.ToArray.Length.Should().Be(values.Length); + sut.ToArray.Should().ContainInOrder(values); + ListOfExtensions.AnyTrue(sut).Should().Be(expected); + } + + [TestMethod] + [DataRow(new bool[] { }, true)] + [DataRow(new bool[] { false }, false)] + [DataRow(new bool[] { false, false, true }, false)] + [DataRow(new bool[] { false, true, false }, false)] + [DataRow(new bool[] { true }, true)] + [DataRow(new bool[] { true, true }, true)] + [DataRow(new bool[] { true, true, false }, false)] + public void VerifyAllTrueExtension(bool[] values, bool expected) + { + ListOf sut = values.BuildList(); + + sut.Should().NotBeNull(); + sut.ToArray.Length.Should().Be(values.Length); + sut.ToArray.Should().ContainInOrder(values); + ListOfExtensions.AllTrue(sut).Should().Be(expected); + } } \ No newline at end of file From e334c16ca8cb620b90b324c49744374831ce4219 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Thu, 21 Aug 2025 14:18:21 +0100 Subject: [PATCH 13/18] Remove unused using statement --- Mark-CSharp/WhyFunctional/ListOf.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Mark-CSharp/WhyFunctional/ListOf.cs b/Mark-CSharp/WhyFunctional/ListOf.cs index 3941857..c3525b4 100644 --- a/Mark-CSharp/WhyFunctional/ListOf.cs +++ b/Mark-CSharp/WhyFunctional/ListOf.cs @@ -1,5 +1,3 @@ -using System.Numerics; - namespace WhyFunctional; public class ListOf From 20ec7aa7912145e0327fcae3f2f8f9cb64ccb01a Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Thu, 21 Aug 2025 14:21:32 +0100 Subject: [PATCH 14/18] Add changes for Copy/Append - Exercise 7 --- .../ListOfExtensionsTests.cs | 40 +++++++++++++++++++ Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 27 ++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs index eb29a4d..2787726 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -86,4 +86,44 @@ public void VerifyAllTrueExtension(bool[] values, bool expected) sut.ToArray.Should().ContainInOrder(values); ListOfExtensions.AllTrue(sut).Should().Be(expected); } + + [TestMethod] + [DataRow(new int[] { })] + [DataRow(new int[] { 1 })] + [DataRow(new int[] { 10, 20, 30 })] + public void VerifyCopyExtension(int[] values) + { + ListOf sut = values.BuildList(); + + ListOf copy = sut.Copy(); + + sut.Should().NotBeNull(); + copy.Should().NotBeNull(); + copy.Should().NotBeSameAs(sut); + copy.ToArray.Length.Should().Be(sut.ToArray.Length); + copy.ToArray.Should().ContainInOrder(sut.ToArray); + } + + [TestMethod] + [DataRow(new int[] { }, new int[] { })] + [DataRow(new int[] { 1 }, new int[] { 2 })] + [DataRow(new int[] { 1 }, new int[] { })] + [DataRow(new int[] { }, new int[] { 2 })] + [DataRow(new int[] { 1, 11 }, new int[] { 2, 22 })] + public void VerifyAppendExtension(int[] lhs, int[] rhs) + { + ListOf first = lhs.BuildList(); + ListOf second = rhs.BuildList(); + + first.Should().NotBeNull(); + second.Should().NotBeNull(); + + int[] expectedAppend = lhs.Concat(rhs).ToArray(); + + ListOf sut = first.Append(second); + + sut.Should().NotBeNull(); + sut.ToArray.Length.Should().Be(expectedAppend.Length); + sut.ToArray.Should().ContainInOrder(expectedAppend); + } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs index fccd86e..9c0a913 100644 --- a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -101,7 +101,7 @@ public static ListOf BuildList(this T[] values) /// The second value /// The logical OR product private static bool LogicalOr(bool a, bool b) => a || b; - + /// /// Provides a logical AND operation for two values /// @@ -123,4 +123,29 @@ public static ListOf BuildList(this T[] values) /// A object with boolean values /// True if ALL values are true, otherwise false public static bool AllTrue(ListOf values) => values.FoldR(LogicalAnd, true); + + /// + /// Copies the contents of into a new + /// + /// The list to be copied + /// + /// A copy of + public static ListOf Copy(this ListOf list) + where T : notnull + { + return list.FoldR(ListOf.Cons, ListOf.Nil()); + } + + /// + /// Appends the contents of to , returnin a new + /// + /// The first part of the + /// The second part of the + /// + /// A new object containing elements of both and + public static ListOf Append(this ListOf first, ListOf second) + where T : notnull + { + return first.FoldR(ListOf.Cons, second); + } } \ No newline at end of file From 50337364035606559935600a56d016c9daa40209 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Thu, 21 Aug 2025 14:38:43 +0100 Subject: [PATCH 15/18] Add Length extension - Exercise 8 --- .../ListOfExtensionsTests.cs | 14 ++++++++++++++ Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 19 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs index 2787726..4267a9f 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -110,6 +110,7 @@ public void VerifyCopyExtension(int[] values) [DataRow(new int[] { 1 }, new int[] { })] [DataRow(new int[] { }, new int[] { 2 })] [DataRow(new int[] { 1, 11 }, new int[] { 2, 22 })] + [DataRow(new int[] { 1, 2 }, new int[] { 3, 4 })] public void VerifyAppendExtension(int[] lhs, int[] rhs) { ListOf first = lhs.BuildList(); @@ -126,4 +127,17 @@ public void VerifyAppendExtension(int[] lhs, int[] rhs) sut.ToArray.Length.Should().Be(expectedAppend.Length); sut.ToArray.Should().ContainInOrder(expectedAppend); } + + [TestMethod] + [DataRow(new int[] { }, 0)] + [DataRow(new int[] { 1 }, 1)] + [DataRow(new int[] { 1, 1 }, 2)] + [DataRow(new int[] { 1, 1, 1, 1 }, 4)] + public void VerifyLengthExtension(int[] values, int expectedLength) + { + ListOf sut = values.BuildList(); + + sut.Should().NotBeNull(); + sut.Length().Should().Be(expectedLength); + } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs index 9c0a913..3acca87 100644 --- a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -137,7 +137,7 @@ public static ListOf Copy(this ListOf list) } /// - /// Appends the contents of to , returnin a new + /// Appends the contents of to , returning a new /// /// The first part of the /// The second part of the @@ -148,4 +148,21 @@ public static ListOf Append(this ListOf first, ListOf second) { return first.FoldR(ListOf.Cons, second); } + + /// + /// Helper method to increment a counter for the length of a + /// + /// N/A + /// The counter value + /// + /// The incremented value of the counter + private static int Count(T left, int right) => ++right; + + /// + /// Determine the length of the + /// + /// The object to be measured + /// + /// The number of objects in the + public static int Length(this ListOf list) where T : notnull => list.FoldR(Count, 0); } \ No newline at end of file From 0fcd883cdb0709dc2500d32fdbc0fea5e4433332 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Thu, 21 Aug 2025 15:14:48 +0100 Subject: [PATCH 16/18] Implement DoubleAll extension - Exercise 9 --- .../ListOfExtensionsTests.cs | 20 ++++++++++++++ Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 27 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs index 4267a9f..bee4c3f 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -140,4 +140,24 @@ public void VerifyLengthExtension(int[] values, int expectedLength) sut.Should().NotBeNull(); sut.Length().Should().Be(expectedLength); } + + [TestMethod] + [DataRow(new int[] { })] + [DataRow(new int[] { 1 })] + [DataRow(new int[] { 1, 2 })] + [DataRow(new int[] { 1, 2, 3 })] + [DataRow(new int[] { 4, 3, 2, 1 })] + public void VerifyDoubleAllExtension(int[] values) + { + ListOf initial = values.BuildList(); + int[] expected = values.Select(v => 2 * v).ToArray(); + + initial.Should().NotBeNull(); + + ListOf sut = initial.DoubleAll(); + sut.Should().NotBeNull(); + sut.Should().NotBeSameAs(initial); + sut.ToArray.Length.Should().Be(expected.Length); + sut.ToArray.Should().ContainInOrder(expected); + } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs index 3acca87..16a22d0 100644 --- a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -165,4 +165,31 @@ public static ListOf Append(this ListOf first, ListOf second) /// /// The number of objects in the public static int Length(this ListOf list) where T : notnull => list.FoldR(Count, 0); + + /// + /// Helper method to return the double of + /// + /// The value to be doubled + /// + /// The equivalent of 2* + private static T Double(T value) where T : INumber => value + value; + + /// + /// Helper method to double the value of the current object + /// + /// The value of the current + /// The contents of the current + /// + /// A new instance of , with the value doubled + private static ListOf Double(T head, ListOf tail) where T : INumber => + ListOf.Cons(Double(head), tail); + + /// + /// A method to double the values contained within and return a new object + /// + /// The object to bew iterated over + /// + /// A new object with the contents of , with their respective values doubled + public static ListOf DoubleAll(this ListOf list) where T : INumber => + list.FoldR(Double, ListOf.Nil()); } \ No newline at end of file From 42cdb59a0ca7a902c000192f7d0d51c4069cf17a Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Thu, 21 Aug 2025 15:56:30 +0100 Subject: [PATCH 17/18] Implement Map extension, update DoubleAll to use Map and include another example using Map: TripleAll - Exercise 10 --- .../ListOfExtensionsTests.cs | 38 +++++++++++++++++ Mark-CSharp/WhyFunctional/ListOfExtensions.cs | 41 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs index bee4c3f..17f1ad4 100644 --- a/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs +++ b/Mark-CSharp/WhyFunctional.Tests/ListOfExtensionsTests.cs @@ -160,4 +160,42 @@ public void VerifyDoubleAllExtension(int[] values) sut.ToArray.Length.Should().Be(expected.Length); sut.ToArray.Should().ContainInOrder(expected); } + [TestMethod] + [DataRow(new int[] { })] + [DataRow(new int[] { 1 })] + [DataRow(new int[] { 1, 2 })] + [DataRow(new int[] { 1, 2, 3 })] + [DataRow(new int[] { 4, 3, 2, 1 })] + public void VerifyDoubleAllExExtension(int[] values) + { + ListOf initial = values.BuildList(); + int[] expected = values.Select(v => 2 * v).ToArray(); + + initial.Should().NotBeNull(); + + ListOf sut = initial.DoubleAllEx(); + sut.Should().NotBeNull(); + sut.Should().NotBeSameAs(initial); + sut.ToArray.Length.Should().Be(expected.Length); + sut.ToArray.Should().ContainInOrder(expected); + } + [TestMethod] + [DataRow(new int[] { })] + [DataRow(new int[] { 1 })] + [DataRow(new int[] { 1, 2 })] + [DataRow(new int[] { 1, 2, 3 })] + [DataRow(new int[] { 4, 3, 2, 1 })] + public void VerifyTripleAllExtension(int[] values) + { + ListOf initial = values.BuildList(); + int[] expected = values.Select(v => 3 * v).ToArray(); + + initial.Should().NotBeNull(); + + ListOf sut = initial.TripleAll(); + sut.Should().NotBeNull(); + sut.Should().NotBeSameAs(initial); + sut.ToArray.Length.Should().Be(expected.Length); + sut.ToArray.Should().ContainInOrder(expected); + } } \ No newline at end of file diff --git a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs index 16a22d0..d820a13 100644 --- a/Mark-CSharp/WhyFunctional/ListOfExtensions.cs +++ b/Mark-CSharp/WhyFunctional/ListOfExtensions.cs @@ -192,4 +192,45 @@ private static ListOf Double(T head, ListOf tail) where T : INumber /// A new object with the contents of , with their respective values doubled public static ListOf DoubleAll(this ListOf list) where T : INumber => list.FoldR(Double, ListOf.Nil()); + + /// + /// A method to map the function over the contents of , returning a new object + /// + /// The to be manipulated + /// The function to manipulate the individual values + /// + /// + /// The new object with manipulated values + private static ListOf Map(this ListOf list, Func fn) + where T : notnull + where TResult : notnull + { + return list.FoldR((head, tail) => ListOf.Cons(fn(head), tail), ListOf.Nil()); + } + + /// + /// A refactored method to double the values contained within and return a new object, using the method + /// + /// The object to bew iterated over + /// + /// A new object with the contents of , with their respective values doubled + public static ListOf DoubleAllEx(this ListOf list) + where T : INumber => list.Map(Double); + + /// + /// Helper method to treble the input + /// + /// The value to be trebled + /// + /// The equivalent of 3* + private static T Triple(T value) where T: INumber => value + Double(value); + + /// + /// Method to treble all values within , returning a new object + /// + /// The values to be trebled + /// + /// AQ new , with values which are triple the original + public static ListOf TripleAll(this ListOf list) + where T : INumber => list.Map(Triple); } \ No newline at end of file From 5901e74c3afe096aa74272664677899f6e1a6528 Mon Sep 17 00:00:00 2001 From: Mark Allott Date: Tue, 2 Sep 2025 15:28:52 +0100 Subject: [PATCH 18/18] updated gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ce89292..29bbcb7 100644 --- a/.gitignore +++ b/.gitignore @@ -416,3 +416,4 @@ FodyWeavers.xsd *.msix *.msm *.msp +whyfp90.pdf