4

MAP に値を追加するとき、Scala がこのステートメントを機能させるために括弧ブロックを追加する必要があるのはなぜですか?

コンパイルしません:

vmap += (item.getName(), item.getString()) // compiler output: "found: String"

ただし、これはコンパイルします:

vmap += ((item.getName(), item.getString())) // note the second set of enclosures

ティア

編集: vmap は次のように定義されます

val vmap = new mutable.HashMap[String, String]()

エピローグ: この編集の時点で、考えられる 2 つの説明を詳述する投稿があり、どちらにも真実の要素が含まれているようです。どれが実際に正しいですか?断言はできませんが…私は言語をまだ学んでいる最中の人間です。ということで、感覚で回答の選択を変えました1 つの答えが (少なくともある程度) 他の答えの中に含まれているということです。そのため、答えを探している他の誰かにとってより広い意味を提供すると思うので、より大きな図を選択しました。皮肉なことに、言語の小さなニュアンスのいくつかを平らにする方法をよりよく理解しようとしていたところ、思っていたよりも多くのニュアンスがあることに気づきました。私はそれが悪いことだと言っているわけではありません - 実際 (IMO) それは柔軟で複雑な言語から期待されるべきです- しかし、それは確かに男がアセンブリの白黒の世界を時々恋しく思うようにします. ...

これを最後に描くために、いくつかの観察:
1) 選択した回答には、Scala のブレインベンダーでいっぱいの Web サイトへのリンクが含まれています (これは、言語で前述のクォークのいくつかを理解しようとするのに非常に役立ちました。) 強くお勧めします。
2)私は別の興味深いひねりに出くわしました-一重括弧(上記の例)は機能しませんが、次のように変更するとうまく機能します...

vmap += ("foo" -> "bar")

これはおそらく、メソッド/関数のシグネチャの一致と関係がありますが、それは私の推測にすぎません。

4

2 に答える 2

7

受け入れられた答えは実際には間違っています。

Map.+= のタプルが得られない理由は、メソッドが 2 つ以上の引数を取る 2 番目のメソッドでオーバーロードされているためです。

コンパイラは、引数の数が間違っている場合にのみタプルを試みます。しかし、2 つの引数を指定し、2 つを取るメソッドがある場合は、型チェックに失敗したとしても、それが選択されます。

何かがうまくいくまで、すべての可能な組み合わせを試し始めません。(Cf 陰関数)

scala> def f(p: Pair[String, Int]) = { val (s,i) = p; s.toInt + i }
f: (p: Pair[String,Int])Int

scala> f("2",3)  // ok to tuple
res0: Int = 5

scala> trait F { def +=(p: Pair[String, Int]) = f(p) }
defined trait F

scala> val foo = new F {}
foo: F = $anon$1@6bc77f62

scala> foo += ("2",3)  // ok to tuple
res1: Int = 5

scala> trait G { def +=(p: Pair[String, Int]) = f(p); def +=(p:(String,Int),q:(String,Int),r:(String,Int)*) = f(p)+f(q)+(r map f).sum }
defined trait G

scala> val goo = new G {}
goo: G = $anon$1@183aeac3

scala> goo += ("2",3)    // sorry
<console>:12: error: type mismatch;
 found   : String("2")
 required: (String, Int)
              goo += ("2",3)
                      ^

scala> goo += (("2",3),("4",5),("6",7))
res3: Int = 27

あなたと私の友人である -Xlint については言及しません。これは、引数の不適切な適応について警告します。

apm@mara:~/tmp$ skala -Xlint
Welcome to Scala version 2.11.0-20130811-132927-95a4d6e987 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def f(p: Pair[String, Int]) = { val (s,i) = p; s.toInt + i }
f: (p: Pair[String,Int])Int

scala> f("2",3)
<console>:9: warning: Adapting argument list by creating a 2-tuple: this may not be what you want.
        signature: f(p: Pair[String,Int]): Int
  given arguments: "2", 3
 after adaptation: f(("2", 3): (String, Int))
              f("2",3)
               ^
res0: Int = 5

scala> List(1).toSet()
<console>:8: warning: Adapting argument list by inserting (): this is unlikely to be what you want.
        signature: GenSetLike.apply(elem: A): Boolean
  given arguments: <none>
 after adaptation: GenSetLike((): Unit)
              List(1).toSet()
                           ^
res3: Boolean = false

適応の危険性については、Adaptive Reasoning puzzlerとこの新しいパズルを参照してください。括弧の有無は主にスタイルの問題であり、括弧が本当に重要な場合、それらを間違って使用すると型エラーが発生することがわかっているため、かなり一般的です。 .

オーバーロードの存在下でのタプルの適応:

scala> class Foo {
     | def f[A](a: A) = 1    // A can be (Int,Int,Int)
     | def f[A](a: A, a2: A) = 2
     | }
defined class Foo

scala> val foo = new Foo
foo: Foo = Foo@2645d22d

scala> foo.f(0,0,0)
<console>:10: warning: Adapting argument list by creating a 3-tuple: this may not be what you want.
        signature: Foo.f[A](a: A): Int
  given arguments: 0, 0, 0
 after adaptation: Foo.f((0, 0, 0): (Int, Int, Int))
              foo.f(0,0,0)
                   ^
res9: Int = 1
于 2013-08-14T23:19:10.673 に答える
4

Map +=メソッドはTuple2[A, B]as パラメータを取るためです。

Tuple2[A, B]を括弧で囲んでマークする必要があります。そうしないと、コンパイラは型を に推論しませんTuple2

ATuple2は単純なペア A -> B です。

val x = (5, 7); // the type is inferred to Tuple2[Int, Int];

反復するMapと、これがさらに明確になります。

map.foreach { case (key, value) => ...};

(key, value)// is a Tuple2

括弧の最初のセットは、コンパイラによって無意味/スキップ可能として扱われます。括弧を囲む 2 番目のセットは、必要な を作成しますTuple2

val x = (item.getName(), item.getString());//Tuple2[String, String]
vmap += x; // THIS COMPILES

vmap += (item.getName(), item.getString())// is identical to 
vmap += item.getName(), item.getString() // now it thinks you are adding a String, the result of item.getName()

vmap += (  (item.getName(), item.getString())   )// skips the first set, sees the Tuple, compiles.

SLS から:

後置演算子は中置演算子よりも優先順位が低いため、 foo bar baz = foo.bar(baz)

この場合:vmap += (item.getName(), item.getString()); = vmap.+=(item.getName());

于 2013-08-14T17:50:22.477 に答える