どう書く?.orgの「文字列の均等分割」問題をC#(2.0)で解いてみました。文字列を指定された数に除算、剰余算を使わないでなるべく均等に分割するという問題です。出題者の方の
この問題は、除算だけでははく算術演算とか、文字列の長さをstrlenの類いで測るとかをしなくても、多分書けるのではないかと思います。
という発言を意識して解いてみました。長さの測定は使用せずに済みましたが、算術演算(インクリメント、デクリメント)がどうしても消せませんでした。以下がそのコードです。
using System; using System.Collections.Generic; class Program { public static void Main() { string s = "82e482df82e682e882e082cd82a982c882ab90a282cc82c882a982f082c882b082ab82ed82d182c281T82a082a982b582ad82e782b782d982c782c98el8c8e8f\82e682d082c982e082c882e882ca82ea82ce96d882cc82b582bd82ad82e782aa82e882e082c482e482ad"; Divide(4, s); Divide(5, s); Divide(6, s); } static void Divide(int n, string s) { IEnumerator<char> ci = s.GetEnumerator(); for (int i = 0; i < n; i++) { foreach(char c in Sub(ci, Skip(i, n, s))) { Console.Write(c); } Console.WriteLine(); } } static IEnumerable<char> Skip(int offset, int n, string s) { int i = 0; foreach (char c in s.Substring(offset)) { if (i == 0) { yield return c; i = n; } i--; } } static IEnumerable<S> Sub<S, T>(IEnumerator<S> iterator, IEnumerable<T> counter) { IEnumerator<T> ci = counter.GetEnumerator(); while (ci.MoveNext()) { if (! iterator.MoveNext()) { break; } yield return iterator.Current; } } }
戦略としては
- 何文字ごとに分割するかを求める
- 部分文字列を作成する
というオーソドックスなものです。C#2.0にはIEnumerable<T>を入出力とするようなユーティリティメソッド群がライブラリに用意されていないため、どうしても自作メソッド内でインクリメント、デクリメントを使うことになってしまいました。Pythonでいうrange()、enumerate()、cycle()あたりがあればインクリメント、デクリメントも使用しないで書けると思うのですが。