LoginSignup
10
10

More than 5 years have passed since last update.

GroovyのListのtransposeメソッドで、zipとかcollectionWithIndexとか

Posted at

はじめに

あまり使う機会のないと思っていたプログラミング言語GroovyのListのtransposeメソッドだけれど、とても便利だったと気づいた、というお話です。
他の言語にある、zipとかcollectionWithIndexとかを実装できます。

transposeメソッドは便利

transposeとの出会い

Groovyは、Object、Collection、ListなどのJavaのクラス・インターフェースに、たくさんの便利なメソッドが追加されています。collect,find,findAllやpermutationsなどを知った時は、「これはこのような使い方ができそうだ、便利だ!」と感動したのを覚えています。

そんな中で、あまり使い方が思い浮かばなかったメソッドがあります。Listに追加されているtransposeメソッドです。「転置行列」を作ると紹介されていました。長さNのListをM個要素に持つListを、長さMのListをN個要素に持つListに変換し、要素の位置を転置する(?)。

コードを見た方が早いですね。

Listのtransposeメソッド
assert [["A", "B"], ["C", "D"], ["E", "F"]].transpose() == [["A", "C", "E"], ["B", "D", "F"]]
assert [[1, 2, 3], [4, 5, 6]].transpose() == [[1, 4], [2, 5], [3, 6]]

「なるほど、転置行列か。」これを見たときには、「使えるとしたら画像処理や数値計算を行う時くらいかな」と考えていました。

transposeとの再会

ちょっと前に、【C#,LINQ】インデックス付きで射影(Select)と抽出(Where)【iが欲しい!?】他の言語もちょっと。というタイトルの投稿をしました。C#のLINQにはコレクションの射影と抽出を行うのに、コレクションの要素とそのインデックスを用いることができるメソッド(のオーバーロード)があります。この投稿では、Groovyについても同様のメソッドがあるかを調べました。書籍やGDKのリファレンスを調べたのですが、(eachWithIndexは見つけたのですが)、Groovyの標準APIには射影・抽出をインデックスとともにできるものは見つけられませんでした。

しかし、こちらで、自分が探していたメソッドを独自で定義し、Listクラスに追加している例を見つけることができました。C#ではインデックス付きで射影を行うメソッドはSelectというメソッド名なのですが、Groovyの場合、collectWithIndexなどがいいのでしょうかね。

collectWithIndexの使用例
def list = ["A", "B", "C", "D", "E", "F"]
assert list.collectWithIndex{ str, index -> "$str$index"} == ["A0", "B1", "C2", "D3", "E4", "F5"]

こちらでのcollectWithIndexメソッドの実装はいくつかあったのですが、その中にtransposeメソッドを使っているものがありました。そのサンプルでのtransposeメソッドの使い方を応用すれば、(他の言語にあるような)Listの便利なメソッドを奇麗に実装することができると思いました。

transposeメソッドを使う前に

長さNのListを2個要素に持つListは作りやすい

def hoge = ["h", "o", "g", "e", "h", "o", "g", "e"]
def fuga = ["f", "u", "g", "a", "f", "u", "g", "a"]

def listOfList = [hoge, fuga]

Groovyは、リテラルとしてリストが提供されています。(外側のリストの)長さが2のリストのリストは上記のように、非常に簡潔に書けますね。

長さ2のListをN個要素に持つListは扱いやすい

次のコードは、長さが2のListを4個要素に持つListの使用例です。collectメソッドを使っています。

assert [["A", 0], ["B", 1], ["C", "2"], ["D", 3], ["E", 4]].collect{ str, num -> "$str$num" } == ["A0", "B1", "C2", "D3", "E4"]

上記のように、このcollectメソッドでは、内部リストの要素が、それぞれクロージャーの引数に代入されて、処理をしやすくなっていますね。

transposeメソッドで作りやすいリストを扱いやすく変換

長さNのListを2個要素に持つListは作りやすく、長さ2のListをN個要素に持つListは扱いやすいので、transposeメソッドを用いて、長さNのListを2個要素に持つListを長さ2のListをN個要素に持つListに変換することを考えます。

def hoge = ["h", "o", "g", "e", "h", "o", "g", "e"]
def fuga = ["f", "u", "g", "a", "f", "u", "g", "a"]

[hoge, fuga].transpose().each { h, f ->
    println "$h $f"
}

出力結果は、次のようになります。

h f
o u
g g
e a
h f
o u
g g
e a

下記は、Rangeも使って、transposeメソッドで、インデックスと共に要素を射影する処理です。

def list = ["h", "o", "g", "e", "f", "u", "g", "a"]

def collectedWithIndex = [list, 0..<list.size()].transpose().collect { e, i -> "$e$i"}

assert collectedWithIndex == ["h0", "o1", "g2", "e3", "f4", "u5", "g6", "a7"]

transposeメソッド使って、他の言語のListの便利メソッドを追加

metaClassを使って、Listクラスに、他の言語の良く使うListの便利なメソッドを追加してみました。

transposeメソッド使って、他の言語のListの便利メソッドを追加
// C#のSelect(インデックス付き)
List.metaClass.collectWithIndex = { body ->
    [delegate, 0..<(delegate.size())].transpose().collect(body)
}

assert list.collectWithIndex{ str, index -> "$str$index"} == ["A0", "B1", "C2", "D3", "E4"]


// ScalaのzipWithIndex
List.metaClass.zipWithIndex = {
    [delegate, 0..<(delegate.size() - 1)].transpose().collect{ new Tuple(it.toArray()) }
}

assert list.zipWithIndex() ==  [["A", 0], ["B", 1], ["C", 2], ["D", 3], ["E", 4]]


// ScalaやC#のzip
List.metaClass.zip = { other ->
    [delegate, other].transpose()
}

assert ["f", "u", "g", "a"].zip(["h", "o", "g", "e"]) == [["f", "h"], ["u", "o"], ["g", "g"], ["a", "e"]]

transposeを要素数が異なるListのListに使ったら(おまけ)

assert [[1, 2, 3],[4, 5, 6],[7, 8]].transpose() == [[1, 4, 7], [2, 5, 8]]
assert [[1, 2, 3],[4, 5, 6],[7]].transpose() == [[1, 4, 7]]
assert [[1, 2, 3],[4, 5],[7, 8]].transpose() == [[1, 4, 7], [2, 5, 8]]

まとめ

transposeメソッドは、「画像処理や数値計算だけに使えるもの」ではなくて、どのようなプログラムでもListを扱うものであれば活躍の機会がある、という考えに変わりました。

transposeメソッド便利!

10
10
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10