「第5回 オフラインリアルタイムどう書く」へなちょこ解答(Java)
ここ最近、やる気がモガの森に家出していますが、先週金曜は「第5回 オフラインリアルタイムどう書く」へ行ってきました。
今回は、大貧民です!
有難いことに、初心者向けの部分問題が用意されていました。しかし、それすら1時間で解き終わりませんでした。悔しい(^q^)
いつも通りJavaで回答しました。全文は長いので、リンク先にあります。
ハイライト(?)
組み合わせの列挙
list.size()==n個の中から、r個のものを選び出すコードです。
protected List<String> loop(List<String> list, int r) { List<String> result = new ArrayList<>(); int n = list.size(); // n = r のとき、選び方は1つなので、リストをそのまま1つのリストにして返す if (n == r) { StringBuilder sb = new StringBuilder(); for (String card : list) { sb.append(card); } result.add(new String(sb)); return result; } // r = 1 のとき、選び方はn通りあるので、リストの要素をそれぞれリストにして返す if (r == 1) { for (String card : list) { result.add(card); } return result; } // r = 0 または r がリストの要素数より大きいとき、空リストを返す if (r == 0 || n < r) { return result; } // 「リストの先頭要素を除いたリストからr-1個を選ぶ組合せのそれぞれに先頭要素を加えたもの // と、 リストの先頭要素を除いたリストからr個を選ぶ組合せ」の合計 List<String> paraList = new ArrayList<>(list); String removed = paraList.remove(0); List<String> looped = loop(paraList, r - 1); for (int i = 0; i < looped.size(); i++) { String str = looped.get(i); looped.set(i, str + removed); } List<String> looped2 = loop(paraList, r); for (String card : looped2) { looped.add(card); } return looped; }
長時間悩みましたが、次のページで公式が自然言語で整理されていたおかげで、なんとか書けました。
カンマ区切り
リストの各要素(の文字列)をカンマ区切りで繋いだ値を得たいとき、Javaで書くと長くなりがちです。
protected String createCombination(List<String> list, int target) { StringBuilder result = new StringBuilder(); List<String> loop = loop(list, target); for (String cards : loop) { result.append(cards); result.append(','); } return new String(result).substring(0, result.length() - 1); }
Javaには、Rubyのjoinのような関数がないからですね。
GoogleのGuavaライブラリに、いい感じのメソッドがあります。
import com.google.common.base.Joiner; // 中略 protected String createCombination(List<String> list, int target) { List<String> loop = loop(list, target); return Joiner.on(",").join(loop); }
非人間的なreturn文から開放されました。
関数オブジェクトみたいなアレ
たとえば、リストの各要素(の文字列)に、ある文字列を足したいとき。
// 「リストの先頭要素を除いたリストからr-1個を選ぶ組合せのそれぞれに先頭要素を加えたもの // と、 リストの先頭要素を除いたリストからr個を選ぶ組合せ」の合計 List<String> paraList = new ArrayList<>(list); String removed = paraList.remove(0); List<String> looped = loop(paraList, r - 1); for (int i = 0; i < looped.size(); i++) { String str = looped.get(i); looped.set(i, str + removed); } List<String> looped2 = loop(paraList, r); for (String card : looped2) { looped.add(card); }
ちょっと勘弁して欲しい感じですが、これもGuavaを使うと、少し幸せ(か?)
// 「リストの先頭要素を除いたリストからr-1個を選ぶ組合せのそれぞれに先頭要素を加えたもの // と、 リストの先頭要素を除いたリストからr個を選ぶ組合せ」の合計 List<String> paraList = new ArrayList<>(list); final String removed = paraList.remove(0); List<String> looped = loop(paraList, r - 1); Function<String, String> func = new Function<String, String>() { public String apply(String input) { return input += removed; }; }; Lists.transform(looped, func); List<String> looped2 = loop(paraList, r); for (String cardList : looped2) { looped.add(cardList); }
あれ? おかしい……何か、不幸になってる気がする……。(もっと良い方法がありそう)
コード以外の話
書こう!
Perlの達人のHさんが、「ラクダ本? 読んでませんよ。あんな分厚いの読んでたらいつまでたっても書けない」と(冗談か本当か分かりませんが)おっしゃっていました。
本やマニュアルをよむのも大事だけど、書くのが大事なんだなぁと改めて思いました。
書き直そう!
「解くのはいいけど、書き直してないでしょ」と鍋谷さんからツッ込まれたので、人様の解答のロジックを理解して、書き直したいところ。
質問しよう!
新卒1年目の方に、同じ会社の先輩が、「せっかくの機会なんだからもっと質問して」「質問された側を困らせて」と繰り返しおっしゃっていたのが印象的でした。
「質問せい」と言ってくれる先輩と勉強会で同席できるなんて、滅茶苦茶心強い状況だと思いますので、本当に何でも質問なさるといいと思いました。
また、会話に出てきた専門用語を後で調べようともし思ったら、メモを取るのがオススメです。覚えていられると思っても、お酒が入ると忘れる場合もありますし。
とはいうものの、2年前のふつける読書会では自分もほとんど質問できなかったので、いま質問するようになったのは、モヒカンな師匠による「思ったことは何でも口に出せ」という教えの賜物だと思います。(素晴らしい言葉ですね!)
というわけで、今回も楽しかったです。ありがとうございました。またよろしくお願いします :)