web-dev-qa-db-ja.com

「case」無名関数はScalaで実際にどのように機能しますか?

親愛なるScala、

_scala> val f1: ((Int, Int)) => Int = { case (a, b) => a + b }
f1: ((Int, Int)) => Int = <function1>

scala> val f2: (Int, Int) => Int = { case (a, b) => a + b }
f2: (Int, Int) => Int = <function2>
_

え?!

_scala> f1(1, 2)
res2: Int = 3
_

OK...

_scala> def takesIntInt2Int(fun: (Int, Int) => Int) = fun(100, 200)
takesIntInt2Int: (fun: (Int, Int) => Int)Int

scala> def takesTuple2Int(fun: ((Int, Int)) => Int) = fun(100, 200)
takesTuple2Int: (fun: ((Int, Int)) => Int)Int

scala> takesIntInt2Int(f2)
res4: Int = 300

scala> takesIntInt2Int(f1)
<console>:10: error: type mismatch;
 found   : ((Int, Int)) => Int
 required: (Int, Int) => Int
              takesIntInt2Int(f1)
                              ^

scala> takesTuple2Int(f1)
res6: Int = 300

scala> takesTuple2Int(f2)
<console>:10: error: type mismatch;
 found   : (Int, Int) => Int
 required: ((Int, Int)) => Int
              takesTuple2Int(f2)
_

正しい。そして今、これを見てください!

_scala> takesTuple2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
 found   : (T1, T2, T3)
 required: (Int, Int)
              takesTuple2Int { case (a, b, c) => a + b + c }
                                    ^

scala> takesIntInt2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
 found   : (T1, T2, T3)
 required: (Int, Int)
              takesIntInt2Int { case (a, b, c) => a + b + c }
_

のように、srsly? o_Oどちらもrequired: (Int, Int)エラーになります。

では、なぜそのような無名関数でcaseを使用するのでしょうか。

24
Michal Rus

Scalaリファレンス( http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf )のセクション8.5を参照してください。式{ case (a, b) => a + b }は、予想されるタイプに基づいて異なる方法で解釈されます。_f1_の定義では、PartialFunction[(Int, Int), Int]にキャストされるFunction1[(Int, Int), Int]が作成されました。 _((Int, Int)) => Int_に対し、_f2_の定義では、_Function2[Int, Int, Int]_、つまり_(Int, Int) => Int_が作成されました。

これらの2つの解釈は、無名関数で一般的にユースケースを使用する2つの状況に関連しています。

1つは、_f1_で行ったように、タプルを受け入れてそのコンポーネントで動作する無名関数を作成するためのものです。例としては、foreachmapまたはMapメソッドに渡す関数があります。 Map(1 -> 2, 3 -> 4) map { case (k, v) => k + v }

2つ目は、唯一のパラメーターでmatchを実行する無名関数を作成するためのものです。あなたの_f2_はこれを行っていますが、有用な方法ではありません。例として、collectに渡される無名関数があります。 List(1, -2, 3) collect { case x if x > 0 => -x }

この2つを組み合わせることができることに注意してください。つまり、_f1_のような関数は複雑なマッチングも実行できます。たとえば、Map(1 -> 2, 3 -> 4) collect { case (k, v) if k < 2 => v }

編集:_res2_はタプリングのために機能します。アプリケーションがチェックを入力しない場合、コンパイラーは失敗する前にタプルで引数をラップしようとします。

しかし、それはアプリケーションのためだけに試みられています。あなたが発見したように、それは一般的な変換ではありません。値_Function2[A, B, C]_をFunction1[(A, B), C]にアップグレードしようとはしません。

17