Logic Dice このページをアンテナに追加

2010-12-27

Set + Tuple = ?

この記事はScala Advent Calendar (http://atnd.org/events/10683)の27日のものです。


簡単な記事です。

さて、以下の式はどの様な結果を生成するでしょうか?

Set() + (1,2)

プログラマ(私)の意図としては、Set( (1,2) ) ((1,2)の1要素タプルを持つ集合)を作って欲しかった一文です。

しかしながら、この文は予想に反してSet(1,2) (1, 2を持つSet[Int]型の集合)を生成します。


なぜでしょうか?

その理由を知るためにSet型の+メソッドを見てみましょう。

すると、以下の様なオーバーロードが存在することが分かります。

def + (elem1: A, elem2: A, elems: A*) : Set[A]

def + (elem: A) : Set[A]

http://www.scala-lang.org/api/current/scala/collection/Set.html

もうお分かりだとは思いますが、scala演算子メソッド呼び出しの置き換えです。

その際、引数の()が1つ*1である場合は、ドットを省略出来るようです。(不確かなので、正確な情報を知っている方教えて下さい)

// 検証コード
(1 to 10) sortWith (_>_)     // OK
(1 to 10) foldLeft (0) (_+_) // NG
(1 to 10).foldLeft (0) (_+_) // OK

要するに、Set() + (1,2)は Set().+(1,2)に置き換えられており、その結果としてSet(1,2)が生成されていた訳です。

コレと同様のミスは型を指定しているコードではコンパイルエラーとなって現われます。

Setの要素として(Int, Int)を要求しているにも関わらず、メソッド呼び出しで2つのIntを与えているためにコンパイルエラーに成るわけです。

scala> val set = Set[(Int, Int)]()
scala> set + (1,2)
<console>:8: error: type mismatch;
 found   : Int(1)
 required: (Int, Int)
       set + (1,2)

私が使っている限りでは、foldLeftなどのループ処理中にSet要素を追加したい事が結構あったので、そこでハマった現象です。

解決作としては、以下の方法が考えられます。

// 1) 一度タプルを変数に代入する
scala> val v = (1,2)
scala> Set() + v
res33: scala.collection.immutable.Set[(Int, Int)] = Set((1,2))

// 2) タプルである事を更に括弧を付けて示す
scala> Set() + ((1,2))
res34: scala.collection.immutable.Set[(Int, Int)] = Set((1,2))

// 3) 仮引数を使って括弧内の要素を分かり安くする
scala> Set() + (elmt = (1,2))
res35: scala.collection.immutable.Set[(Int, Int)] = Set((1,2))

// 4) タプルの別表現を用いる
// thanks id:xuweiさん
scala> Set() + Tuple2(1, 2)
scala> Set() + (1 -> 2)
res36: scala.collection.immutable.Set[(Int, Int)] = Set((1,2))

私的には1)が良いのではないかと思います。

単純にタプルを渡すだけのメソッドでも、同様のミスが現われる可能性は大きいと言えます。

そのため、タプルをメソッドに渡す場合、一時的な変数を用意して利用する(1)のパターンを利用する)と安全でしょう。

追記:また、タプルか引数かの曖昧さを排除した4)も良いと思います。

*1Scalaではカリー化によって2つ以上の括弧による関数定義が可能

xuweixuwei 2010/12/27 20:23 Set() + (1 -> 2)

っていうのもありかと。

あと
>引数の()が1つである場合は、ドットを省略
っていうのは、たぶん正確には、1か0の場合ですかね。

これ↓
(1 to 10) foldLeft (0) (_+_) // NG

も、ドットつけないで、こんなふうに↓カッコつけても
( (1 to 10) foldLeft (0) ) (_+_)

コンパイル通ります

a-hisamea-hisame 2010/12/28 02:02 xuweiさん

> (1 -> 2)
ありですね。
追記しておきます。

> ドットと括弧
んー、全体の引数のための括弧が1以下の場合は.を省略しても良い、なんですかねー?
それと結果を見る限りはそうだと思うんですけど……。
ちゃんと.の省略に関する資料を探した方が良いのかなー…

kiyoshihosodakiyoshihosoda 2010/12/28 21:17 Tuple(1,2)は@deprecatedみたいですね。

kiyoshihosodakiyoshihosoda 2010/12/28 21:18 すみません、Tuple2(1,2)は大丈夫でした・・・

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/a-hisame/20101227/1293446036