// 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]}"); // Must be correct length, so this is false Console.WriteLine($"numbers is [1, 3]: {numbers is [1, 3]}"); // Can use wildcards 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, _, _]}"); // Really don't care about length? Use .. 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]}"); // ... and this is false 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]) { Console.WriteLine($"Head: {x}"); Console.WriteLine($"Tail: {xs}"); } // So we can do nice functional-style stuff: Console.WriteLine($"Sum of numbers: {Sum(numbers)}"); // Talking about spans -- the second new pattern matching feature in // C# 11 is related to spans and strings. 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))}"); 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 { [] => 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] => [(x1 + x2) / 2, .. Downsample(xs)], _ => [], }; // 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(), _ => [], }; // C# collection expressions and spread operators are surprisingly powerful, // but the syntax can end up a bit complex. // [ .. exp ] works for exp:IEnumerable and is shorter than .ToList() // We still need the Select function from LINQ // Target typing means that the expression may only be accepted by the compiler // once it is complete and correct. static List> Transpose_(List> s) => s switch { [[_, ..], ..] => [ [.. s.Select(x => x[0])], .. Transpose_([.. s.Select(x => x[1..])]), ], _ => [], }; // Construction helpers can result in an implementation closer to the original static List Map(Func f, List l) => [.. l.Select(f)]; static T Head(List l) => l[0]; static List Tail(List l) => l[1..]; static List Cons(T head, List tail) => [head, .. tail]; static List> Transpose__(List> s) => s switch { [[_, ..], ..] => Cons(Map(Head, s), Transpose__(Map(Tail, s))), _ => [], }; #endregion } }