SEND + MORE = MONEY
using System; using System.Collections.Generic; using System.Linq; class Program { static int ToInt(string s, int[] map) { int x = 0; foreach (var c in s) { x = 10 * x + map[c]; if (x == 0 && s.Length > 1) return -1; // leading zero } return x; } static void Solve_1(int p, char[] cs, int used, int[] map, string[] ss) { if (p == cs.Length) { var a = ss.Take(ss.Length - 1).Select(e => ToInt(e, map)); if (a.Contains(-1)) return; // 不正な変換結果 int b = ToInt(ss.Last(), map); if (a.Sum() == b) { Console.WriteLine("{0} = {1}", string.Join(" + ", ss.Take(ss.Length - 1)), ss.Last()); Console.WriteLine("{0} = {1}", string.Join(" + ", a), b); } return; } for (int i = 0; i < 10; i++) { if ((used & (1 << i)) != 0) continue; map[cs[p]] = i; // cs[p] に i を割り当てる Solve_1(p + 1, cs, used | (1 << i), map, ss); } } static void Solve(params string[] ss) { var cs = new HashSet<char>(ss.SelectMany(e => e)).ToArray(); if (cs.Length > 10) throw new Exception(); // 10 種類より多いと数字を割り当てられない var map = new int[128]; // どの文字にどの数字を割り当てたか Solve_1(0, cs, 0, map, ss); Console.WriteLine(); } static void Main() { Solve("SEND", "MORE", "MONEY"); Solve("CROSS", "ROADS", "DANGER"); Solve("SIX", "SEVEN", "SEVEN", "TWENTY"); } }
実行結果です。
$ time mono a.exe SEND + MORE = MONEY 9567 + 1085 = 10652 CROSS + ROADS = DANGER 96233 + 62513 = 158746 SIX + SEVEN + SEVEN = TWENTY 650 + 68782 + 68782 = 138214 mono a.exe 5.38s user 0.03s system 99% cpu 5.419 total
リンク
文字列の配列を文字のシーケンスに変換する
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