From a257e8e8246151e4573a471d659a216650abf37f Mon Sep 17 00:00:00 2001 From: David Arno Date: Thu, 1 Jun 2017 14:49:17 +0100 Subject: [PATCH] # 36 - Further cons pattern matching features added and some refactoring. Empty, Single and Cons (latter two with Where support) are now supported and the code has been refactored to be a lot less "hacky" --- src/SuccincT/Functional/ConsEnumerable.cs | 8 +- .../PatternMatchers/ConsFuncMatcher.cs | 110 +++++++++++++++--- .../PatternMatchers/IConsFuncConsHandler.cs | 12 ++ .../IConsFuncConsWhereHandler.cs | 11 ++ .../PatternMatchers/IConsFuncMatcher.cs | 2 + .../IConsFuncSingleWhereHandler.cs | 5 +- .../PatternMatchers/IConsSingleHandler.cs | 4 +- .../PatternMatchers/ConsFuncMatcherTests.cs | 71 ++++++++++- 8 files changed, 196 insertions(+), 27 deletions(-) create mode 100644 src/SuccincT/PatternMatchers/IConsFuncConsHandler.cs create mode 100644 src/SuccincT/PatternMatchers/IConsFuncConsWhereHandler.cs diff --git a/src/SuccincT/Functional/ConsEnumerable.cs b/src/SuccincT/Functional/ConsEnumerable.cs index e862d43..2bf6183 100644 --- a/src/SuccincT/Functional/ConsEnumerable.cs +++ b/src/SuccincT/Functional/ConsEnumerable.cs @@ -42,7 +42,7 @@ private ConsEnumerable(IEnumerable enumeration, IEnumerable head) => } }; - private ConsEnumerable(ConsNode node) => + internal ConsEnumerable(ConsNode node) => _node = new ConsNode { State = StartNode, @@ -57,6 +57,8 @@ private ConsEnumerable() => private IEnumerator GetEnumerator() => new ConsNodeEnumerator(_node); + internal ConsNodeEnumerator GetTypedEnumerator() => new ConsNodeEnumerator(_node); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -68,7 +70,7 @@ IConsEnumerable IConsEnumerable.Cons(IEnumerable head) => ConsResult IConsEnumerable.Cons() { - using (var enumerator = GetEnumerator() as ConsNodeEnumerator) + using (var enumerator = GetTypedEnumerator()) { return enumerator.MoveNext() ? new ConsResult(enumerator.Current, new ConsEnumerable(enumerator.Node.Next)) @@ -78,7 +80,7 @@ ConsResult IConsEnumerable.Cons() internal (T head, IConsEnumerable tail) TupleCons() { - using (var enumerator = GetEnumerator() as ConsNodeEnumerator) + using (var enumerator = GetTypedEnumerator()) { return enumerator.MoveNext() ? (enumerator.Current, new ConsEnumerable(enumerator.Node.Next)) diff --git a/src/SuccincT/PatternMatchers/ConsFuncMatcher.cs b/src/SuccincT/PatternMatchers/ConsFuncMatcher.cs index 0306399..437a0ae 100644 --- a/src/SuccincT/PatternMatchers/ConsFuncMatcher.cs +++ b/src/SuccincT/PatternMatchers/ConsFuncMatcher.cs @@ -1,29 +1,42 @@ -using System; +using SuccincT.Functional; +using SuccincT.Options; +using System; using System.Collections.Generic; -using System.Linq; namespace SuccincT.PatternMatchers { internal class ConsFuncMatcher : IConsFuncMatcher, IConsFuncNoneHandler, IConsFuncSingleHandler, - IConsFuncSingleWhereHandler + IConsFuncSingleWhereHandler, + IConsFuncConsHandler, + IConsFuncConsWhereHandler { - private readonly IEnumerable _collection; private TResult _emptyValue; - private Func _whereFunc; - private List<(Func testFunc, TResult value)> _singleTests; + private Func _singleWhereFunc; + private Func, bool> _consWhereFunc; + private readonly List<(Func testFunc, Func doFunc)> _singleTests; + private readonly List<(Func, bool> testFunc, + Func, TResult> doFunc)> _simpleConsTests; + private readonly ConsNodeEnumerator _enumerator; internal ConsFuncMatcher(IEnumerable collection) { - _collection = collection; - _singleTests = new List<(Func testFunc, TResult value)>(); + _enumerator = new ConsEnumerable(collection).GetTypedEnumerator(); + _singleTests = new List<(Func, Func)>(); + _simpleConsTests = new List<(Func, bool>, Func, TResult>)>(); } IConsFuncNoneHandler IConsFuncMatcher.Empty() => this; IConsFuncSingleHandler IConsFuncMatcher.Single() => this; + IConsFuncConsHandler IConsFuncMatcher.Cons() => this; - TResult IConsFuncMatcher.Result() => _collection.Count() == 0 ? _emptyValue : SingleMatch(); + TResult IConsFuncMatcher.Result() => + TryCons(_enumerator).Match().To() + .Where((h, _) => !h.HasValue).Do(_emptyValue) + .Where((_, t) => t == null).Do((h, _) => SingleMatch(h.Value)) + .Else((h, t) => ConsMatch(h.Value, t)) + .Result(); IConsFuncMatcher IConsFuncNoneHandler.Do(TResult value) { @@ -33,31 +46,90 @@ IConsFuncMatcher IConsFuncNoneHandler.Do(TResult value) IConsFuncMatcher IConsFuncSingleHandler.Do(TResult value) { - _singleTests.Add((x => true, value)); + _singleTests.Add((_ => true, _ => value)); return this; } - IConsFuncSingleWhereHandler IConsFuncSingleHandler.Where(Func testFunc) + IConsFuncMatcher IConsFuncSingleHandler.Do(Func doFunc) { - _whereFunc = testFunc; + _singleTests.Add((_ => true, doFunc)); return this; } IConsFuncMatcher IConsFuncSingleWhereHandler.Do(TResult value) { - _singleTests.Add((_whereFunc, value)); + _singleTests.Add((_singleWhereFunc, _ => value)); + return this; + } + + IConsFuncMatcher IConsFuncSingleWhereHandler.Do(Func doFunc) + { + _singleTests.Add((_singleWhereFunc, doFunc)); return this; } - private TResult SingleMatch() + + IConsFuncMatcher IConsFuncConsHandler.Do(Func, TResult> doFunc) { - foreach(var (testFunc, value) in _singleTests) + _simpleConsTests.Add(((x, y) => true, doFunc)); + return this; + } + + IConsFuncMatcher IConsFuncConsHandler.Do(TResult value) + { + _simpleConsTests.Add(((x, y) => true, (x, y) => value)); + return this; + } + + IConsFuncMatcher IConsFuncConsWhereHandler.Do(Func, TResult> doFunc) + { + _simpleConsTests.Add((_consWhereFunc, doFunc)); + return this; + } + + IConsFuncMatcher IConsFuncConsWhereHandler.Do(TResult value) + { + _simpleConsTests.Add((_consWhereFunc, (x, y) => value)); + return this; + } + + IConsFuncSingleWhereHandler IConsFuncSingleHandler.Where(Func testFunc) + { + _singleWhereFunc = testFunc; + return this; + } + + IConsFuncConsWhereHandler IConsFuncConsHandler.Where(Func, + bool> testFunc) + { + _consWhereFunc = testFunc; + return this; + } + + private TResult SingleMatch(T head) + { + foreach (var (testFunc, doFunc) in _singleTests) { - if (testFunc(_collection.First())) - { - return value; - } + if (testFunc(head)) return doFunc(head); } return default(TResult); } + + private TResult ConsMatch(T head, IConsEnumerable tail) + { + foreach (var (testFunc, doFunc) in _simpleConsTests) + { + if (testFunc(head, tail)) return doFunc(head, tail); + } + return default(TResult); + } + + private static (Option head, ConsEnumerable tail) TryCons(ConsNodeEnumerator enumerator) + { + if (!enumerator.MoveNext()) return (Option.None(), null); + + var head = enumerator.Current; + var tailNode = enumerator.Node.Next; + return enumerator.MoveNext() ? (head, new ConsEnumerable(tailNode)) : (head, null); + } } } \ No newline at end of file diff --git a/src/SuccincT/PatternMatchers/IConsFuncConsHandler.cs b/src/SuccincT/PatternMatchers/IConsFuncConsHandler.cs new file mode 100644 index 0000000..6626b30 --- /dev/null +++ b/src/SuccincT/PatternMatchers/IConsFuncConsHandler.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace SuccincT.PatternMatchers +{ + public interface IConsFuncConsHandler + { + IConsFuncConsWhereHandler Where(Func, bool> testFunc); + IConsFuncMatcher Do(TResult value); + IConsFuncMatcher Do(Func, TResult> doFunc); + } +} \ No newline at end of file diff --git a/src/SuccincT/PatternMatchers/IConsFuncConsWhereHandler.cs b/src/SuccincT/PatternMatchers/IConsFuncConsWhereHandler.cs new file mode 100644 index 0000000..9e93813 --- /dev/null +++ b/src/SuccincT/PatternMatchers/IConsFuncConsWhereHandler.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace SuccincT.PatternMatchers +{ + public interface IConsFuncConsWhereHandler + { + IConsFuncMatcher Do(TResult value); + IConsFuncMatcher Do(Func, TResult> doFunc); + } +} \ No newline at end of file diff --git a/src/SuccincT/PatternMatchers/IConsFuncMatcher.cs b/src/SuccincT/PatternMatchers/IConsFuncMatcher.cs index 71d6c27..d187563 100644 --- a/src/SuccincT/PatternMatchers/IConsFuncMatcher.cs +++ b/src/SuccincT/PatternMatchers/IConsFuncMatcher.cs @@ -6,6 +6,8 @@ public interface IConsFuncMatcher IConsFuncSingleHandler Single(); + IConsFuncConsHandler Cons(); + TResult Result(); } } \ No newline at end of file diff --git a/src/SuccincT/PatternMatchers/IConsFuncSingleWhereHandler.cs b/src/SuccincT/PatternMatchers/IConsFuncSingleWhereHandler.cs index 6442af1..51a5031 100644 --- a/src/SuccincT/PatternMatchers/IConsFuncSingleWhereHandler.cs +++ b/src/SuccincT/PatternMatchers/IConsFuncSingleWhereHandler.cs @@ -1,7 +1,10 @@ -namespace SuccincT.PatternMatchers +using System; + +namespace SuccincT.PatternMatchers { public interface IConsFuncSingleWhereHandler { IConsFuncMatcher Do(TResult value); + IConsFuncMatcher Do(Func doFunc); } } \ No newline at end of file diff --git a/src/SuccincT/PatternMatchers/IConsSingleHandler.cs b/src/SuccincT/PatternMatchers/IConsSingleHandler.cs index d844669..28954bb 100644 --- a/src/SuccincT/PatternMatchers/IConsSingleHandler.cs +++ b/src/SuccincT/PatternMatchers/IConsSingleHandler.cs @@ -4,8 +4,8 @@ namespace SuccincT.PatternMatchers { public interface IConsFuncSingleHandler { - IConsFuncMatcher Do(TResult value); - IConsFuncSingleWhereHandler Where(Func testFunc); + IConsFuncMatcher Do(TResult value); + IConsFuncMatcher Do(Func doFunc); } } \ No newline at end of file diff --git a/tests/SuccincT.Tests/SuccincT/PatternMatchers/ConsFuncMatcherTests.cs b/tests/SuccincT.Tests/SuccincT/PatternMatchers/ConsFuncMatcherTests.cs index 719f6e0..4c69799 100644 --- a/tests/SuccincT.Tests/SuccincT/PatternMatchers/ConsFuncMatcherTests.cs +++ b/tests/SuccincT.Tests/SuccincT/PatternMatchers/ConsFuncMatcherTests.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; -using NUnit.Framework; +using NUnit.Framework; using SuccincT.PatternMatchers; +using System.Collections.Generic; +using System.Linq; using static NUnit.Framework.Assert; namespace SuccincTTests.SuccincT.PatternMatchers @@ -30,6 +31,17 @@ public void SingleItemList_CanBeMatchedWithSingle() IsTrue(result); } + [Test] + public void SingleItemList_CanBeMatchedWithSingleWithResultViaLambda() + { + var list = new List { 1 }; + var result = list.Match().To() + .Empty().Do(false) + .Single().Do(x => x == 1) + .Result(); + IsTrue(result); + } + [Test] public void SingleItemList_CanBeMatchedWithWhere() { @@ -41,6 +53,17 @@ public void SingleItemList_CanBeMatchedWithWhere() IsTrue(result); } + [Test] + public void SingleItemList_CanBeMatchedWithWhereWithResultViaLambda() + { + var list = new List { 1 }; + var result = list.Match().To() + .Single().Where(x => x == 1).Do(x => x == 1) + .Single().Do(false) + .Result(); + IsTrue(result); + } + [Test] public void SingleItemList_CanBeMatchedWhenWhereDoesntMatch() { @@ -51,5 +74,49 @@ public void SingleItemList_CanBeMatchedWhenWhereDoesntMatch() .Result(); IsFalse(result); } + + [Test] + public void TwoItemList_CanBeMatchedWithCons() + { + var list = new List { 1, 2 }; + var result = list.Match().To() + .Cons().Do((h, t) => h + t.First()) + .Result(); + AreEqual(3, result); + } + + [Test] + public void TwoItemList_CanBeMatchedWithConsUsingValue() + { + var list = new List { 1, 2 }; + var result = list.Match().To() + .Empty().Do(false) + .Single().Do(false) + .Cons().Do(true) + .Result(); + IsTrue(result); + } + + [Test] + public void MultiItemList_CanBeMatchedWithConsWithWhere() + { + var list = new List { 1, 2, 3, 4 }; + var result = list.Match().To() + .Cons().Where((h, t) => t.Count() == 3).Do((x, y) => 1) + .Cons().Do(2) + .Result(); + AreEqual(1, result); + } + + [Test] + public void MultiItemList_CanBeMatchedWithConsWithWhereUsingValue() + { + var list = new List { 1, 2, 3, 4 }; + var result = list.Match().To() + .Cons().Where((h, t) => t.Count() == 3).Do(1) + .Cons().Do(2) + .Result(); + AreEqual(1, result); + } } }