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

ブログに久しぶりにGroovyのコードが載った気がする。


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

複数の文字列を受け取り、受け取った文字列をそれぞれ先頭から見てゆき、
すべてが同じ内容であれば除去した内容の文字列を返却する関数を書いて下さい。
※関数の引数と戻り値については複数の文字列が受け渡しできれば型や方法は
問いません。

例1)hoge("abcdef", "abc123")
戻り値 => "def" と "123" ("abc" が除去される)

例2)hoge("あいうえお", "あいさんさん", "あいどる")
戻り値 => "うえお", "さんさん", "どる" ("あい" が除去される)

例3)hoge("12345", "67890", "12abc")
戻り値 => "12345", "67890", "12abc" (一致なしのため、そのまま返却される)

テストコード

// お題
assert hoge('abcdef', 'abc123') == ['def', '123']
assert hoge("あいうえお", "あいさんさん", "あいどる") == ["うえお", "さんさん", "どる"]
assert hoge("12345", "67890", "12abc") == ["12345", "67890", "12abc" ]


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

解答

def hoge(String... args) {
    work = args.toList()
    work.sort()[0..-2].each {
        for (i in 0 .. args*.size().min()-1 ) {
            m =  ( it[0 .. i] =~ work[-1][0 .. i++] )
            if(m.find()) {
                del = m[0]
            }
        }   
    }
    args*.replaceFirst(~/$del/,"")
}

// お題のテスト
assert hoge('abcdef', 'abc123') == ['def', '123']
assert hoge("あいうえお", "あいさんさん", "あいどる") == ["うえお", "さんさん", "どる"]
assert hoge("12345", "67890", "12abc") == ["12345", "67890", "12abc" ]

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

メモ

1行目 def hoge(String... args)

Java SE 5の可変引数で導入されてた可変引数(可変長引数)も、もちろん使える。

Groovy物語 Story #8 メソッド?クロージャ?でも、少し触れられてる。

2行目 work = args.toList()

toList():配列をListに変換 プログラミングGROOVY 184

3行目 work.sort()[0..-2].each {

sort():collectionの各要素をソート プログラミングGROOVY p87
[0..-2]
.. :繰り返しの範囲の指定 プログラミングGROOVY p81
-2 :末尾から2番目の要素 プログラミングGROOVY p82

4行目 for (i in 0 .. args*.size().min()-1 )

for文: in で指定もできる。 プログラミングGROOVY p108
args*.:展開ドット コレクションの全ての要素に対して適用できる プログラミングGROOVY p94
size():Javaの場合は、length()、size()とバラバラだが、Groovyはsize()で統一されている。 プログラミングGROOVY p182
min(): Conllectionの要素のうち最小のものを抽出 プログラミングGROOVY p86

5行目 m = ( it[0 .. i] =~ work[-1][0 .. i++] )

=~: 正規表現、Matcherオブジェクトの生成 プログラミングGROOVY p114
[-1]:末尾の要素 プログラミングGROOVY p82

6行目 if(m.find())

Matcherオブジェクトの評価 プログラミングGROOVY p115

7行目 del = m[0]

マッチした要素の取り出し。Java正規表現APIの機能。
正規表現にマッチした部分を取り出す

11行目 replaceFirst(~/$del/,"")

$del:GString $変数名の展開 プログラミングGROOVY p67
replaceFirst():文字列の置換 replaceAll()での説明がプログラミングGROOVY p117
replaceAll()は全置換、replaceFirst()は最初にマッチしたものだけを置換

15行目 assert

プログラミングGROOVY p32,p111
このような間違ったコードを書く

def hoge(String... args) {
    
    args.sort()[0..-2].each {
        for (i in 0 .. args*.size().min()-1 ) {
            m =  ( it[0 .. i] =~ args[-1][0 .. i++] )
            if(m.find()) {
                del = m[0]
            }
        }   
    }
    args*.replaceFirst(~/$del/,"")
}

すると、以下のようにassert文で失敗する。

Exception thrown
2011/08/15 19:24:34 org.codehaus.groovy.runtime.StackTraceUtils sanitize

警告: Sanitizing stacktrace:

Assertion failed: 

assert hoge('abcdef', 'abc123') == ['def', '123']
       |                        |
       [123, def]               false


	at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:386)

sort()をしてるから、要素の順番が入れ替わっていてNGになっていることがすぐ分かる。
GroovyのPower Assertは、原因の調査がしやすい。



環境

OS:Windows XP
Java:1.6.0_26
Groovy:1.8.0
大阪いくときに、ネットブックにはJava SE7入れて、こっちのマシンにJava SE 7インストールするの忘れてた・・・

添削歓迎

ここ間違ってるよ
こうした方がGroovyらしくないか?などなど
方法は、コメント、トラックバックはてブTwitter @orange_clover宛 で、お願いしまます。




今日からGroovyを始めるための4つのステップ





関谷 和愛、上原 潤二、須江 信洋、中野 靖治
価格: ¥ 3,360
価格は記載時点のものです。購入前にAmazonでご確認ください。


「プログラミングGROOVY」正誤表