Iterate メソッド
初期値と「次の値」を返す関数を引数で受け取り、無限シーケンスを返す Iterate メソッドを作ってみました。
using System; using System.Collections.Generic; using System.Linq; class Iter { public static IEnumerable<T> Iterate<T>(T init, Func<T, T> next) { T x = init; while (true) { yield return x; x = next(x); } } } class Program { static void Display<T>(IEnumerable<T> xs) { Console.WriteLine(string.Join(" ", xs)); } static void Main() { Display(Iter.Iterate(0, e => e + 8).Take(11)); // => 0 8 16 24 32 40 48 56 64 72 80 Display(Iter.Iterate(0, e => e + 8).TakeWhile(e => e <= 80)); // => 0 8 16 24 32 40 48 56 64 72 80 } }
実行結果です。
0 8 16 24 32 40 48 56 64 72 80 0 8 16 24 32 40 48 56 64 72 80
参考
MinBy, MaxBy を作る
using System; using System.Collections.Generic; using System.Linq; public static class Ext { public static T MinBy<T, U>(this IEnumerable<T> xs, Func<T, U> key) where U : IComparable<U> { return xs.Aggregate((a, b) => key(a).CompareTo(key(b)) < 0 ? a : b); } public static T MaxBy<T, U>(this IEnumerable<T> xs, Func<T, U> key) where U : IComparable<U> { return xs.Aggregate((a, b) => key(a).CompareTo(key(b)) > 0 ? a : b); } } class Program { static void Main() { var xs = new List<int[]> { new[] { 1, 2 }, new[] { 20, 50 }, new[] { 30, 22 }, new[] { 50, -100 }, }; // 1 番目の要素が最小 var a = xs.MinBy(e => e[1]); Console.WriteLine(string.Join(" ", a)); // 50 -100 // 1 番目の要素が最大 var b = xs.MaxBy(e => e[1]); Console.WriteLine(string.Join(" ", b)); // 20 50 // 要素数が 1 の場合 var c = new[] { 123 }.MinBy(e => e); Console.WriteLine(c); // 123 // 要素数が 0 の場合 // var d = new int[0].MinBy(e => e); // Console.WriteLine(d); // System.InvalidOperationException: Sequence contains no elements } }
実行結果です。
50 -100 20 50 123
リンク
比較関数を渡せる GroupBy メソッドを作る
比較関数を渡せる GroupBy メソッドを作ってみました。
using System; using System.Collections.Generic; using System.Linq; class Program { static IEnumerable<T[]> GroupBy<T>(IEnumerable<T> xs, Func<T, T, bool> match) { var ls = new List<T>(); foreach (var x in xs) { if (ls.Count == 0 || match(ls[ls.Count-1], x)) { ls.Add(x); } else { yield return ls.ToArray(); ls.Clear(); ls.Add(x); } } if (ls.Count > 0) { yield return ls.ToArray(); } } static void Display<T>(IEnumerable<T[]> xxs) { var xs = xxs.Select(e => string.Format("[{0}]", string.Join(", ", e))); var s = string.Format("[{0}]", string.Join(", ", xs)); Console.WriteLine(s); } static void Main() { Display(GroupBy(new[] { 1, 2, 1, 2, 5, 1 }, (a, b) => a < b)); // => [[1, 2], [1, 2, 5], 1] Display(GroupBy(new[] { 1, 1, 1, 2, 2, 5 }, (a, b) => a == b)); // => [[1, 1, 1], [2, 2], [5] Display(GroupBy(new[] { 1, 1, 1, 2, 2, 5 }, (a, b) => a != b)); // => [[1], [1], [1, 2], [2, 5]] Display(GroupBy("Mississippi", (a, b) => a == b)); // => [[M], [i], [s, s], [i], [s, s], [i], [p, p], [i]] } }
実行結果です。
[[1, 2], [1, 2, 5], [1]] [[1, 1, 1], [2, 2], [5]] [[1], [1], [1, 2], [2, 5]] [[M], [i], [s, s], [i], [s, s], [i], [p, p], [i]]
リンク
1 次元配列の回転
1 次元配列を回転するサンプルです。
using System; using System.Collections.Generic; using System.Linq; class Program { static T[] Rotate<T>(T[] xs, int n) { var ret = new T[xs.Length]; if (ret.Length == 0) return ret; int m = n % ret.Length; for (int i = 0; i < ret.Length; i++) { ret[i] = xs[m++]; if (m == ret.Length) m = 0; } return ret; } static void Main() { var xs = Enumerable.Range(1, 5).ToArray(); for (int i = 0; i < 7; i++) { var ys = Rotate(xs, i); Console.WriteLine(string.Join(" ", ys)); } } }
実行結果です。
1 2 3 4 5 2 3 4 5 1 3 4 5 1 2 4 5 1 2 3 5 1 2 3 4 1 2 3 4 5 2 3 4 5 1
インスタンスが null の場合でも拡張メソッドの呼び出しは可能
インスタンスが null の場合にメソッド呼び出しを行うと、System.NullReferenceException 例外が生成されますが、拡張メソッドの場合は、インスタンスが null でも呼び出しが可能です。
using System; public class Foo { public void Hello() { Console.WriteLine("Hello!!"); } } public static class Extension { public static void Goodbye(this Foo foo) { Console.WriteLine("Goodbye!!"); } } class Program { static void Main() { Foo foo = new Foo(); foo.Hello(); // Hello foo.Goodbye(); // Goodbye!! Foo foo2 = null; // foo2.Hello(); // System.NullReferenceException foo2.Goodbye(); // Goodbye!! } }
実行結果です。
Hello!! Goodbye!! Goodbye!!
Partition 関数
シーケンスの要素を、述語関数を満たす要素と満たさない要素に分割する Partition 関数(メソッド)を作ってみました。
using System; using System.Collections.Generic; using System.Linq; class Program { static Tuple<List<T>, List<T>> Partition<T>(IEnumerable<T> xs, Predicate<T> pred) { var ok = new List<T>(); var ng = new List<T>(); foreach (var x in xs) { if (pred(x)) { ok.Add(x); } else { ng.Add(x); } } return Tuple.Create(ok, ng); } static void Main() { var xs = new[] { 1, 2, 3, 4, 5 }; // 偶数と奇数に分割する var t = Partition(xs, e => e % 2 == 0); Console.WriteLine(string.Join(", ", t.Item1)); // 2, 4 Console.WriteLine(string.Join(", ", t.Item2)); // 1, 3, 5 // ゼロまたは正と負に分割する var t2 = Partition(new[] { 1, -3, 22, -90, -100 }, e => e >= 0); Console.WriteLine(string.Join(", ", t2.Item1)); // 1, 22 Console.WriteLine(string.Join(", ", t2.Item2)); // -3, -90, -100 } }
実行結果です。
2, 4 1, 3, 5 1, 22 -3, -90, -100
リンク
Group 関数
シーケンス内の隣り合う要素のうち、同じ値のものを配列にまとめる関数 Group を作ってみました。
using System; using System.Collections.Generic; using System.Linq; static class Iter { public static IEnumerable<T[]> Group<T>(IEnumerable<T> xs) { var ls = new List<T>(); foreach (var x in xs) { if (ls.Count == 0 || ls[0].Equals(x)) { ls.Add(x); } else { yield return ls.ToArray(); ls.Clear(); ls.Add(x); } } if (ls.Count > 0) { yield return ls.ToArray(); } } } class Program { static void Display<T>(IEnumerable<T[]> xxs) { var ls = new List<string>(); foreach (var xs in xxs) { ls.Add(string.Format("[{0}]", string.Join(", ", xs))); } var s = string.Format("[{0}]", string.Join(", ", ls)); Console.WriteLine(s); } static void Main() { Display(Iter.Group(new[] { 1 })); Display(Iter.Group(new[] { 1, 1 })); Display(Iter.Group(new int[0])); Display(Iter.Group(new[] { 1, 1, 0, 0, 1, 1, 1, 2, 2, 0, 0, 0 })); Display(Iter.Group(new[] { 1, 2, 3, 0, 0 })); } }
実行結果です。
[[1]] [[1, 1]] [] [[1, 1], [0, 0], [1, 1, 1], [2, 2], [0, 0, 0]] [[1], [2], [3], [0, 0]]