No Programming, No Life

新しいNPNLです。http://d.hatena.ne.jp/fumokmm/ から引っ越してきました。

Groovyソースコード斜め読み(その1)「Groovyにもタプルがあった 」

このシリーズの一覧はこちら

はじめに

なんと、無いと思っていたタプル(組)がGroovyにもある模様。しかし、実体はListを薄くラッピングしただけのもののよう…せっかく見つけたので一応ご紹介ということで。
(動作確認: Groovy Version: 1.8.0-rc-1 JVM: 1.6.0_22)

クラス階層

java.lang.Object
    java.util.AbstractCollection<E>
        java.util.AbstractList
            groovy.lang.Tuple

リストとして実装されている。

コンストラクタ

Tuple(Object[] contents) 

クラスをnewするにはオブジェクト配列を渡せばいいようです。オブジェクト配列ってことは要素は何でもアリということですね。

空タプル
def t1 = new Tuple()
assert t1            == []                  , '空リストと同じ'
assert t1.class.name == 'groovy.lang.Tuple' , '型はTuple型'
assert t1.size()     == 0                   , 'サイズはもちろん0'
intの三つ組み
def t2 = new Tuple(1, 2, 3)
assert t2            == [1, 2, 3]           , 'intのリスト'
assert t2.class.name == 'groovy.lang.Tuple' , '型はTuple型'
assert t2.size()     == 3                   , 'サイズは3'
いろんな要素
def t3 = new Tuple('str', 12.345G, ['a', 'b', 'c'], [m: 'map'])
assert t3            == ['str', 12.345G, ['a', 'b', 'c'], [m: 'map']] , 'いろんな要素のリスト'
assert t3.class.name == 'groovy.lang.Tuple'                           , '型はTuple型'
assert t3.size()     == 4                                             , 'サイズは4'

値の取り出し

def t4 = new Tuple(10, 20, 'A')
assert t4.get(0) == 10   , '#get(index)で値を取得'
assert t4[1]     == 20   , '#get(index)のシンタクスシュガー'
assert t4[3]     == null , 'index範囲を超えた場合はnull'
assert t4[-1]    == 'A'  , '単純にリストなのでマイナスインデックスも使えたりする。'

値の取り出しはシンプルに#getメソッドでindexアクセス。

値の代入

def t5 = new Tuple(10, 20)
t5[0] = 30 // => java.lang.UnsupportedOperationException

怒られます。値の再代入は許可されないようですね、そうじゃないとタプルじゃないですもんね。

マップのキーとして使ってみる

def doubleKeyMap = [:]
doubleKeyMap[new Tuple('a', 'b')] = 'A and B'
doubleKeyMap[new Tuple('c', 'd')] = 'C and D'

def getValue = { Tuple t ->
  assert t.size() == 2
  doubleKeyMap[t]
}

assert getValue(new Tuple('a', 'b')) == 'A and B'
assert getValue(new Tuple('b', 'a')) == null

使えた。

FizzBuzzもやってみた(似非パターンマッチ)

def fizz     = new Tuple(true, false)
def buzz     = new Tuple(false, true)
def fizzbuzz = new Tuple(true, true)

def result = (1..100).collect{
  new Tuple(new Tuple(it % 3 == 0, it % 5 == 0), it)
}.collect {
  switch (it[0]) {
    case {it == fizz}     : return 'Fizz'
    case {it == buzz}     : return 'Buzz'
    case {it == fizzbuzz} : return 'FizzBuzz'
    default               : return it[1]
  }
}.join(', ')

println result

できた。

値束縛っぽく使ってみる

def (a, _) = new Tuple(10, 20)
assert a == 10

まとめ

PythonScalaなど、ちゃんとタプルを実装している言語に比べるとやっぱりあまり使い勝手が良くない。無理に使わなくてもGroovyの場合はリストで代用すれば十分なのではないだろうかという印象。将来ロードマップで示されている通り、パターンマッチを言語レベルでサポートするようになったころ、タプルも言語レベルでサポートされるようになるのだろうか。