diff --git a/samples/.idea/.idea.samples/.idea/indexLayout.xml b/samples/.idea/.idea.samples/.idea/indexLayout.xml index 7b08163..717a2b9 100644 --- a/samples/.idea/.idea.samples/.idea/indexLayout.xml +++ b/samples/.idea/.idea.samples/.idea/indexLayout.xml @@ -1,7 +1,9 @@ - + + ../../advanced-pattern-matching-cs-samples + diff --git a/samples/CS11/Program.cs b/samples/CS11/Program.cs index 86162ec..60548ef 100644 --- a/samples/CS11/Program.cs +++ b/samples/CS11/Program.cs @@ -1,25 +1,42 @@ // ReSharper disable All +using System.Text.Json; + namespace CS11 { class Program { static void Main(string[] args) { int[] numbers = { 1, 3, 42 }; + // The following match expressions are "slice patterns" - rather unintuitive naming in my eyes, but what can we do. // Exact match - Console.WriteLine($"numbers is [1,3,42]: {numbers is [1, 3, 42]}"); + Console.WriteLine($"numbers is [1, 3, 42]: {numbers is [1, 3, 42]}"); // Must be correct length, so this is false - Console.WriteLine($"numbers is [1,3]: {numbers is [1, 3]}"); + Console.WriteLine($"numbers is [1, 3]: {numbers is [1, 3]}"); // Can use wildcards - Console.WriteLine($"numbers is [1,3,_]: {numbers is [1, 3, _]}"); + Console.WriteLine($"numbers is [1, 3, _]: {numbers is [1, 3, _]}"); // Still must be correct length! - Console.WriteLine($"numbers is [1,3,_,_]: {numbers is [1, 3, _, _]}"); + Console.WriteLine($"numbers is [1, 3, _, _]: {numbers is [1, 3, _, _]}"); // Really don't care about length? Use .. - Console.WriteLine($"numbers is [..,42]: {numbers is [.., 42]}"); + Console.WriteLine($"numbers is [.., 42]: {numbers is [.., 42]}"); // Cool stuff, match with embedded patterns - this is true - Console.WriteLine($"numbers is [1,3,>10]: {numbers is [1, 3, > 10]}"); + Console.WriteLine($"numbers is [1, 3, >10]: {numbers is [1, 3, > 10]}"); // ... and this is false - Console.WriteLine($"numbers is [1,3,>100]: {numbers is [1, 3, > 100]}"); + Console.WriteLine($"numbers is [1, 3, >100]: {numbers is [1, 3, > 100]}"); + // Other combinations possible, this is true + Console.WriteLine($"numbers is [.. , <= 10, >= 10]: {numbers is [.., <= 10, >= 10]}"); + // The .. operator can be used in the middle as well. It can also match a zero-length part of the list. + int[] moreNumbers = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; + Console.WriteLine( + $"moreNumbers is [<= 50, <= 50, .., > 50, >= 100]: {moreNumbers is [<= 50, <= 50, .., > 50, >= 100]}"); + Console.WriteLine( + $"numbers is [1, 3, .., 42]: {numbers is [1, 3, .., 42]}"); + // Note that .. can be used only once. This is invalid. + // Console.WriteLine( + // $"moreNumbers is [<= 50, <= 50, .., > 50, >= 100]: {moreNumbers is [.., <= 50, <= 50, .., > 50, >= 100]}"); + + // And one more strange combination: + Console.WriteLine($$"""numbers is [1, .. { Length: 2 }]: {{numbers is [1, .. { Length: 2 }]}}"""); // You can split a list into head and tail: if (numbers is [var x, .. var xs]) { @@ -33,20 +50,91 @@ namespace CS11 { // Talking about spans -- the second new pattern matching feature in // C# 11 is related to spans and strings. - string WhatsNext(ReadOnlySpan spanString) => spanString switch { + string WhatsNext(ReadOnlySpan spanString) => spanString switch + { "one" => "two", "two" => "three", _ => "That's it!" }; Console.WriteLine($"One. Next: '{WhatsNext("one".AsSpan())}'"); + + //---------------------------------------------- + //---------------------------------------------- + // WE'LL GET BACK TO THIS DEMO IN A LITTLE WHILE + //---------------------------------------------- + //---------------------------------------------- + + //return; + + #region FP stuff for the second part of the demo + + // Technically, only C# 12 allows collection expressions - we're using them here anyway, for brevity + List values = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0]; + Console.WriteLine($"Downsampled: {JsonSerializer.Serialize(Downsample(values))}"); + + List> m1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + Console.WriteLine($"Transpose: {JsonSerializer.Serialize(Transpose(m1))}"); + Console.WriteLine($"Transpose: {JsonSerializer.Serialize(Transpose_(m1))}"); + + #endregion } // Now we're talking -- note that the use of Span magically solves // a perf problem you would have if you used an Array type here. - static int Sum(Span l) => l switch { + static int Sum(Span l) => l switch + { [] => 0, [var x, .. var xs] => x + Sum(xs) }; + + #region FP stuff for the second part of the demo + + // From F#: + // let rec downsample : float list -> float list = + // function + // | [] -> [] + // | h1::h2::t -> 0.5 * (h1 + h2) :: downsample t + // | [_] -> invalid_arg "downsample" + + static List Downsample(List values) => values switch + { + [var x1, var x2, .. var xs] => new List { (x1 + x2) / 2 }.Concat(Downsample(xs)).ToList(), + _ => [] + }; + + // From F# + // let rec transpose = function + // | (_ :: _) :: _ as xss -> + // List.map List.head xss :: transpose (List.map List.tail xss) + // | _ -> [] + + static List> Transpose(List> s) => s switch + { + [[_, .. _], .. _] => + new List> { s.Select(x => x.First()).ToList() } + .Concat>( + Transpose( + s.Select(x => x.Skip(1).ToList()).ToList() + )).ToList(), + _ => [] + }; + + // Construction helpers: + + static List Map(Func f, List l) => l.Select(f).ToList(); + static T Head(List l) => l[0]; + static List Tail(List l) => l.Skip(1).ToList(); + static List Cons(T head, List tail) => new List { head }.Concat(tail).ToList(); + + // Much closer now! + + static List> Transpose_(List> s) => s switch + { + [[_, .. _], .. _] => Cons(Map(Head, s), Transpose_(Map(Tail, s))), + _ => [] + }; + + #endregion } } \ No newline at end of file diff --git a/samples/CS9/Program.cs b/samples/CS9/Program.cs index c715fce..151f2a2 100644 --- a/samples/CS9/Program.cs +++ b/samples/CS9/Program.cs @@ -34,10 +34,11 @@ namespace CS9 { // Positional patterns with "discard" placeholders - note that // the element lists must be "complete"! static OrderValue OrderValueCategory(Order o) => - o switch { + o switch + { // Labels are optional here but can be useful // for readability - (itemCount: < 0, itemPrice: _) => throw new ArgumentException("Positive itemCounts please!"), + Order (itemCount: < 0, itemPrice: _) => throw new ArgumentException("Positive itemCounts please!"), (_, < 0) => throw new ArgumentException("Positive itemPrices please!"), (>= 100, _) => OrderValue.ValuableDueToHighCount, (_, >= 1000) => OrderValue.ValuableDueToHighItemPrice, @@ -48,7 +49,6 @@ namespace CS9 { _ => OrderValue.NotValuable }; - static void Main(string[] args) { } } diff --git a/samples/Temp/Program.cs b/samples/Temp/Program.cs new file mode 100644 index 0000000..c8f8eaa --- /dev/null +++ b/samples/Temp/Program.cs @@ -0,0 +1,20 @@ +// ReSharper disable InconsistentNaming + +namespace Temp; + +internal class Program { + private static void Main(string[] args) { + Console.WriteLine("Hello, World!"); + } + + private (T, U, T, U) BinarySearch(Func Split, Func Cmp, (T x, U c_x, T y, U c_y) r) { + T m = Split(r.x, r.y); + U c_m = Cmp(m); + return (r.c_x = c_m, c_m = r.c_y) switch + { + (true, false) => (m, c_m, r.y, r.c_y), + (false, true) => (r.x, r.c_x, m, c_m), + _ => throw new Exception("Not found") + }; + } +} \ No newline at end of file diff --git a/samples/Temp/Temp.csproj b/samples/Temp/Temp.csproj new file mode 100644 index 0000000..85b4959 --- /dev/null +++ b/samples/Temp/Temp.csproj @@ -0,0 +1,10 @@ + + + + Exe + net9.0 + enable + enable + + + diff --git a/samples/fpsamples/sample2.hs b/samples/fpsamples/sample2.hs index fbbb80f..df0fb6c 100644 --- a/samples/fpsamples/sample2.hs +++ b/samples/fpsamples/sample2.hs @@ -7,4 +7,4 @@ isEqualFood :: Food -> Food -> Bool isEqualFood Pasta Pasta = True isEqualFood Pizza Pizza = True isEqualFood Chips Chips = True -isEqualFood _ _ = False \ No newline at end of file +isEqualFood _ _ = False diff --git a/samples/fpsamples/sample3.hs b/samples/fpsamples/sample3.hs index 7fb960e..62e13e1 100644 --- a/samples/fpsamples/sample3.hs +++ b/samples/fpsamples/sample3.hs @@ -9,4 +9,4 @@ fact x | x <= 1 = 1 fact' :: Int -> Int fact' 0 = 1 fact' 1 = 1 -fact' x = x * (fact (x - 1)) \ No newline at end of file +fact' x = x * (fact' (x - 1)) diff --git a/samples/fpsamples/sample4.fs b/samples/fpsamples/sample4.fs index 7173b1a..eb09f56 100644 --- a/samples/fpsamples/sample4.fs +++ b/samples/fpsamples/sample4.fs @@ -11,4 +11,5 @@ let rec listLength l = // `match` call. let rec listLength' = function | [] -> 0 - | _ :: xs -> 1 + (listLength' xs) \ No newline at end of file + | _ :: xs -> 1 + (listLength' xs) + diff --git a/samples/samples.sln b/samples/samples.sln index 4d4d035..b85e0c5 100644 --- a/samples/samples.sln +++ b/samples/samples.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CS10", "CS10\CS10.csproj", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CS11", "CS11\CS11.csproj", "{D1B3E6C4-303A-4A76-B555-4D78E50F8957}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Temp", "Temp\Temp.csproj", "{C78DD1B6-35A0-4DAA-85ED-68B06662C523}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,5 +44,9 @@ Global {D1B3E6C4-303A-4A76-B555-4D78E50F8957}.Debug|Any CPU.Build.0 = Debug|Any CPU {D1B3E6C4-303A-4A76-B555-4D78E50F8957}.Release|Any CPU.ActiveCfg = Release|Any CPU {D1B3E6C4-303A-4A76-B555-4D78E50F8957}.Release|Any CPU.Build.0 = Release|Any CPU + {C78DD1B6-35A0-4DAA-85ED-68B06662C523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C78DD1B6-35A0-4DAA-85ED-68B06662C523}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C78DD1B6-35A0-4DAA-85ED-68B06662C523}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C78DD1B6-35A0-4DAA-85ED-68B06662C523}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal