83

この構造がScalaでタイプミスマッチエラーを引き起こすのはなぜですか?

for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

<console>:6: error: type mismatch;
 found   : List[(Int, Int)]
 required: Option[?]
       for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

一部をリストに切り替えると、正常にコンパイルされます。

for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))

これも正常に機能します。

for (first <- Some(1); second <- Some(2)) yield (first,second)
4

5 に答える 5

119

map内包表記は、またはflatMapメソッドの呼び出しに変換されます。たとえば、これは次のとおりです。

for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)

になります:

List(1).flatMap(x => List(1,2,3).map(y => (x,y)))

したがって、最初のループ値(この場合)はメソッド呼び出しList(1)を受け取ります。リターンで別のを返すflatMapのでflatMap、理解のための結果はもちろんになります。(これは私にとっては初めてのことでした。理解のために、必ずしもストリームになるとは限りません。必ずしもsになるとは限りません。)ListListListSeq

さて、でどのようflatMapに宣言されているか見てみましょうOption

def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]

これを覚えておいてください。Some(1)理解の誤り(が付いているもの)がどのように一連のマップ呼び出しに変換されるかを見てみましょう。

Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))

flatMapこれで、呼び出しのパラメーターが、必要に応じて、を返すものであるが、を返すものではないことがList簡単にわかりますOption

問題を修正するには、次のようにします。

for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)

それはうまくコンパイルされます。Optionよくあることですが、これはのサブタイプではないことに注意してくださいSeq

于 2011-01-18T01:38:25.813 に答える
32

覚えやすいヒントです。内包表記では、最初のジェネレーターのコレクションのタイプ(この場合はOption [Int])を返そうとします。したがって、Some(1)から始める場合は、Option[T]の結果を期待する必要があります。

リストタイプの結果が必要な場合は、リストジェネレーターから始める必要があります。

なぜこの制限があり、常に何らかのシーケンスが必要になるとは思わないのですか?あなたは戻ることが理にかなっている状況を持つことができますOption。たぶん、あなたは何かと組み合わせて、次の関数を使っOption[Int]て取得したいものを持っているかもしれません: ; 次に、これを記述して、物事が「意味をなさない」場合はNoneを取得できます。Option[List[Int]](i:Int) => if (i > 0) List.range(0, i) else None

val f = (i:Int) => if (i > 0) Some(List.range(0, i)) else None
for (i <- Some(5); j <- f(i)) yield j
// returns: Option[List[Int]] = Some(List(0, 1, 2, 3, 4))
for (i <- None; j <- f(i)) yield j
// returns: Option[List[Int]] = None
for (i <- Some(-3); j <- f(i)) yield j
// returns:  Option[List[Int]] = None

一般的なケースで内包表記を拡張する方法は、実際には、型のオブジェクトをM[T]関数と組み合わせて型(T) => M[U]のオブジェクトを取得するためのかなり一般的なメカニズムM[U]です。あなたの例では、Mはオプションまたはリストにすることができます。一般に、同じタイプである必要がありMます。したがって、オプションとリストを組み合わせることはできません。可能性のある他の例については、このトレイトのサブクラスをM見てください。

リストを始めたとき、なぜ仕事List[T]と組み合わせるのですか?(T) => Option[T]この場合、ライブラリは意味のあるより一般的なタイプを使用します。したがって、ListをTraversableと組み合わせることができ、OptionからTraversableへの暗黙の変換があります。

肝心なのはこれです:式が返すタイプを考えて、最初のジェネレーターとしてそのタイプから始めてください。必要に応じてそのタイプで包みます。

于 2011-01-18T05:23:57.330 に答える
4

それはおそらく、オプションが反復可能ではないことと関係があります。暗黙的Option.option2Iterableには、コンパイラが2番目がIterableであることを期待している場合を処理します。ループ変数の種類によってコンパイラの魔法は違うと思います。

于 2011-01-18T01:41:53.073 に答える
1

私はいつもこれが役に立ちました:

scala> val foo: Option[Seq[Int]] = Some(Seq(1, 2, 3, 4, 5))
foo: Option[Seq[Int]] = Some(List(1, 2, 3, 4, 5))

scala> foo.flatten
<console>:13: error: Cannot prove that Seq[Int] <:< Option[B].
   foo.flatten
       ^

scala> val bar: Seq[Seq[Int]] = Seq(Seq(1, 2, 3, 4, 5))
bar: Seq[Seq[Int]] = List(List(1, 2, 3, 4, 5))

scala> bar.flatten
res1: Seq[Int] = List(1, 2, 3, 4, 5)

scala> foo.toSeq.flatten
res2: Seq[Int] = List(1, 2, 3, 4, 5)
于 2018-06-18T22:34:56.427 に答える
0

Scala2.13オプションが作成されてからIterableOnce

sealed abstract class Option[+A] extends IterableOnce[A] with Product with Serializable

したがって、理解のための以下は、option2Iterable暗黙の変換を使用せずに機能します

scala> for {
     |   a <- List(1)
     |   b <- Some(41)
     | } yield (a + b)
val res35: List[Int] = List(42)

scala> List(1).flatMap
final override def flatMap[B](f: Int => scala.collection.IterableOnce[B]): List[B]

私たちが見るところList#flatMapは、関数を取りますIterableOnce。理解のために上記の脱糖は次のようなものになります

List(1).flatMap(a => Some(41).map(b => a + b))

これは、暗黙の変換がないことを示しています。

ただし、Scala 2.12以前Optionでは、トラバース可能/反復可能なエンティティではありませんでした

sealed abstract class Option[+A] extends Product with Serializable 

したがって、理解のための上記は、次のようなものに脱糖します

List(1).flatMap(a => option2Iterable(Some(41)).map(b => a + b))(List.canBuildFrom[Int])

ここで、暗黙の変換が見られます。

それが逆に機能しない理由は、理解のために始まりOption、それから私たちは連鎖しようとしますList

scala> for {
     |   a <- Option(1)
     |   b <- List(41)
     | } yield (a + b)
         b <- List(41)
           ^
On line 3: error: type mismatch;
        found   : List[Int]
        required: Option[?]

scala> Option(1).flatMap
final def flatMap[B](f: Int => Option[B]): Option[B]

なぜならOption#flatMap、関数をに取り、にOption変換するListことは、複数の要素を持つsのOption要素を失うため、おそらく意味がないからです。List

szeiger説明するように

最近のOption変更により、暗黙的な変換が不要になったため、実際には内包表記のユースケースが理解しやすくなっていると思います。のRHSはrequiresであるため、任意のコレクションタイプOptionのRHSで使用できます(ただし、のRHSはrequiresであるため、その逆ではありません)。flatMapIterableOnceOption#flatMapOption

于 2021-04-10T15:50:33.003 に答える