日々常々

ふつうのプログラマがあたりまえにしたいこと。

「お題:文字列を先頭から見て同じところまで除去」をやってみた

お題:文字列を先頭から見て同じところまで除去 - No Programming, No Life

前からちょっとやってみたいなーと思ってたfumokmmさんの「お題」の新しいのが出たのでやってみました。
出題者の土俵であるGroovyでやるあたり、向こう見ずな感じ。

思った以上に苦労した上にGroovyらしさのかけらもない所からスタートしました。だいたいの経緯は gistに上げてます のでそちらを参照くださいませ。

def hoge(String... values) {
    def i = -1
    while (++i < values*.size().min() && values.every {it[i] == values[0][i]});
    values.collect {
        it.substring i
    }
}

assert hoge('123', '123') == ['','']
assert hoge('112233', '123123') == ['12233', '23123']
assert hoge('なまむぎ', 'なまごめ', 'なまたまご') == ['むぎ', 'ごめ', 'たまご']
assert hoge('abcde', 'abcdefg') == ['', 'fg']
assert hoge('abcdefg', 'abcde') == ['fg', ''] // 追加したテスト


// お題
assert hoge('abcdef', 'abc123') == ['def', '123']
assert hoge("あいうえお", "あいさんさん", "あいどる") == ["うえお", "さんさん", "どる"]
assert hoge("12345", "67890", "12abc") == ["12345", "67890", "12abc" ]
  • Groovyでも可変長引数はそのまま String... と書ける。
    • def... とは書けなかった。
  • Object#eachWithIndex も考えたけどうまい使い方が思い浮かばなかった。
    • 「異なる文字の出た一番小さい index」見たくできる気はするんだけども。
  • なんかいいやり方あると思うんだけど思い浮かばない。
  • そんなわけで一度 for で片付けてしまうことにした。

バグってた!

ありがとうございますー。逆も要りましたか…。
テスト追加してとりあえず対処。一層不格好になってしまた。

  • 最初は第一引数の文字数でループまわしてた(ので指摘の通り例外)。
  • とりあえずループカウンタを超えたらbreakするようにOR条件を追加した。
  • 気に入らなかったので *. して sort して [0] にした。

その後

  • 140文字に収まったよ!(><
  • これは冒頭に貼ってるコード。今のとこコレかなー…。

色々やってみたログ

  • Groovyでは文字列の引き算ができる。
    • assert 'abcde' - 'abc' == 'de'
  • これは minus メソッド
    • つまり *.minus とすれば全部から引ける
  • 「先頭から連続して一致する文字列」がわかればそれを引けば良い
def hoge(String... args) {
    def i = -1, v = ''
    while(++i < args*.size().min() && args.every{it[i] == args[0][i]}) {
        v += args[0][i]
    }
    args*.minus(v)
}
  • 良い案だと思ったのに文字数増えた……
  • ゴルフじゃないんだから。
  • ひっくり返してみた。
    • 引いたときに全部の引数が変わるものって感じ
def hoge(String... args) {
    for (int i = args*.size().min() - 1; i >= 0; i--) {
        def v = args[0][0..i]
        if (args.every { it != it - v }) return args*.minus(v)
    }
    args
}
  • なんかGroovyっぽさが減った気がする…