文字列の配列を文字のシーケンスに変換する
using System; using System.Linq; class Program { static void Main() { var ss = new[] { "foo", "bar", "baz" }; // 文字列の配列を文字のシーケンスにする var cs = ss.SelectMany(e => e); Console.WriteLine(string.Join(" ", cs)); // => f o o b a r b a z // 文字列を反転してから繋げる var cs2 = ss.SelectMany(e => e.Reverse()); Console.WriteLine(string.Join(" ", cs2)); // => o o f r a b z a b // 特定の文字を削除してから繋げる('a' を取り除く) var cs3 = ss.SelectMany(e => e.Where(c => c != 'a')); Console.WriteLine(string.Join(" ", cs3)); // => f o o b r b z } }
実行結果です。
f o o b a r b a z o o f r a b z a b f o o b r b z
リンク
Frequencies メソッドを作る
シーケンスに含まれる要素の出現回数を求める Frequencies メソッドを作ってみました。
using System; using System.Collections.Generic; using System.Linq; static class Ext { public static Dictionary<T, int> Frequencies<T>(this IEnumerable<T> xs) { var d = new Dictionary<T, int>(); foreach (var x in xs) { if (!d.ContainsKey(x)) d[x] = 1; else d[x]++; } return d; } } class Program { static void Test<T>(T[] xs) { var d = xs.Frequencies().Select(e => $"{e.Key} {e.Value}"); Console.WriteLine(string.Join(", ", d)); } static void Main() { Test(new[] { 1, 2, 3, 1, 1, 2, 2 }); // => 1 3, 2 3, 3 1 Test(new[] { "foo", "bar", "foo", "buz" }); // => foo 2, bar 1, buz 1 Test(new[] { Tuple.Create(1, 2), Tuple.Create(1, 2), Tuple.Create(2, 3) }); // => (1, 2) 2, (2, 3) 1 } }
実行結果です。
1 3, 2 3, 3 1 foo 2, bar 1, buz 1 (1, 2) 2, (2, 3) 1
CompositeDisposable クラスの Dispose メソッドを呼び出した後に Add したときの振る舞い
- CompositeDisposable クラスの Dispose メソッドを呼び出すと、CompositeDisposable に格納されている全ての要素に対して Dispose メソッドが呼ばれます。
- CompositeDisposable クラスの Dispose メソッドを呼び出すと、IsDisposed プロパティは true となり、Dispose 済みとなります。
- それ以降は Add メソッドで要素を追加すると、Add した直後にその要素の Dispose メソッドが呼ばれます。
CompositeDisposable を使い回そうとしてはまりました。
using System; using System.Reactive.Disposables; class Foo : IDisposable { public int Id { get; private set; } public Foo(int id) { Id = id; } public void Dispose() { Console.WriteLine($"id:{Id} dispose"); } } class Program { static void Main(string[] args) { var d = new CompositeDisposable(); d.Add(new Foo(1)); d.Add(new Foo(2)); Console.WriteLine("IsDisposed: {0}", d.IsDisposed); // false Console.WriteLine("-- call dispose --"); d.Dispose(); Console.WriteLine("IsDisposed: {0}", d.IsDisposed); // true Console.WriteLine("-- Add --"); d.Add(new Foo(3)); // ただちに Foo.Dispose が呼ばれる Console.WriteLine("-- call dispose --"); d.Dispose(); } }
コンパイル、実行結果です。
% mcs composite-disposable.cs -r:System.Reactive.Core.dll,System.Reactive.Interfaces.dll -out:a.exe % mono a.exe IsDisposed: False -- call dispose -- id:1 dispose id:2 dispose IsDisposed: True -- Add -- id:3 dispose -- call dispose --
リンク
Butlast メソッド
シーケンスの最後の要素を取り除く Butlast メソッドを作ってみました。
using System; using System.Collections.Generic; using System.Linq; public static class Ext { public static IEnumerable<T> Butlast<T>(this IEnumerable<T> xs) { bool first = true; T prev = default(T); foreach (var x in xs) { if (!first) { yield return prev; } prev = x; first = false; } } } class Program { static void Display<T>(IEnumerable<T> xs) { Console.WriteLine("count:{0} [{1}]", xs.Count(), string.Join(" ", xs)); } static void Main() { Display(new[] { 1, 2, 3 }.Butlast()); // count:2 [1 2] Display(new[] { 1, 2 }.Butlast()); // count:1 [1] Display(new[] { 1 }.Butlast()); // count:0 [] Display(Enumerable.Empty<int>().Butlast()); // count:0 [] } }
実行結果です。
count:2 [1 2] count:1 [1] count:0 [] count:0 []
参考
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]]