Scala系列:完全不可预测的行为

Scala系列:完全不可预测的行为,scala,scala-2.8,yield,scala-collections,Scala,Scala 2.8,Yield,Scala Collections,对Scala 2.8的收集行为感到非常失望。问题是:我正在创建一个数独板。我正在标记从A1到I9的单元格(字母是行,数字是列)。我想得到黑板上的单位列表,这是9行,夜间列,和夜间象限 这是我的scala课程: class Square(val row:Char, val column:Int) extends Pair[Char, Int](row, column) { override def toString() = "" + row + column } object Boar

对Scala 2.8的收集行为感到非常失望。问题是:我正在创建一个数独板。我正在标记从A1到I9的单元格(字母是行,数字是列)。我想得到黑板上的单位列表,这是9行,夜间列,和夜间象限

这是我的scala课程:

class Square(val row:Char, val column:Int) extends Pair[Char, Int](row, column) {
    override def toString() = "" + row + column 
}

object Board {
    private val rows = "ABCDEFGHI"
    private val cols = 1 to 9
    private lazy val units = unitList(rows, cols)
    private def cross(rows:Iterable[Char], columns:Iterable[Int]):Iterable[Square] = {
        for (row <- rows; col <- columns)
            yield new Square(row, col)
    }

    private def unitList(rows:Iterable[Char], cols:Iterable[Int]) = {
        val u1 = (for (col <- cols) yield cross(rows, List(col)))
        val u2 = (for (row <- rows) yield cross(List(row), cols))
        val u3 = (for (cols <- List("ABC", "DEF", "GHI"); rows <- List(1 to 3, 4 to 6, 7 to 9)) yield cross(cols, rows))

        u1 :+ u2 :+ u3  // won't compile, reason: :+ is not a member of Iterable[Iterable[sudoku.Square]]
    }

    def run() {
        val u1 = (for (col <- cols) yield cross(rows, List(col)))
        val u2 = (for (row <- rows) yield cross(List(row), cols))
        val u3 = (for (cols <- List("ABC", "DEF", "GHI"); rows <- List(1 to 3, 4 to 6, 7 to 9)) yield cross(cols, rows))
        println(u1)
        println(u2)
        println(u3)
        val u4 = u1 :+ u2 :+ u3  // compiles
        println(u1 :+ u2 :+ u3)  // compiles and output correctly
    }
}

我在这里完全不知所措。

理解中的屈服结果来自第一个生成器的类型,在你的方法中,你限制了你的参数类型,直到你失去了你的方法

//The type of 1 to 9 is show below
scala> 1 to 9
res0: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)


//If you cast it to Iterable[Int] it doens't have the :+ method
scala> (res0:Iterable[Int]) :+ 1
<console>:7: error: value :+ is not a member of Iterable[Int]
       (res0:Iterable[Int]) :+ 1
       ^

//But if you don't, you have it
scala> res0 :+ 1                
res6: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 1)

//And to prove that for comprehensions yield derives the type of the first generator:

scala> for(a <- res0) yield a
res7: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> for(a <- (res0:Iterable[Int])) yield a
res8: Iterable[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9)
//1到9的类型如下所示
scala>1到9
res0:scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne=Range(1,2,3,4,5,6,7,8,9)
//如果将其强制转换为Iterable[Int],则它没有:+方法
scala>(res0:Iterable[Int]):+1
:7:错误:值:+不是Iterable[Int]的成员
(res0:Iterable[Int]):+1
^
//但如果你没有,你就拥有它
scala>res0:+1
res6:scala.collection.immutable.IndexedSeq[Int]=向量(1,2,3,4,5,6,7,8,9,1)
//并证明,对于理解,第一个生成器的类型:

scala>for(a编译器错误发生的原因是
:+
IndexedSeq
(因此
List
)的成员,而不是
Iterable
。如果将
unitList
的返回值从

u1 :+ u2 :+ u3


它编译得很好。

尽管有非常奇怪的行为,
:+
不可能是您想要的运算符。因为您没有注释
单位列表的返回类型,我不知道您期望的是什么。我假设您想要返回
Iterable[Square]
Iterable[Iterable[Square]]
。让我们看看如何获得它们,以及为什么
:+
不正确

首先,
u1
u2
u3
都是
Iterable[Iterable[Square]]
,尽管确切的子类型各不相同。这应该很容易理解:
cross
返回
Iterable[Square]
,因此在a中产生
cross
用于理解的结果是
Iterable[Iterable[Square]]

下面,让我们考虑<代码>:+<代码>。该方法将一个元素添加到集合中,因此,如果<代码> u1 是代码>可迭代(A,B,C)< /代码>,其中A、B和C是代码>可迭代[平方] < /代码>,然后<代码> u1:+u2< /代码>是代码>可迭代(a,b,c,u2)< /代码>,并且其类型变为代码>迭代[x]。
,其中
X
Iterable[Square]
(a、b和c的类型)和
Iterable[Iterable[Square]
(u2
)的统一。最终结果是
Iterable[AnyRef]

由于
u1
u2
u3
的类型基本相同,因此正确的操作很可能是:

u1 ++ u2 ++ u3
它将返回
Iterable[Iterable[Square]]
。现在,如果要删除嵌套并返回
Iterable[Square]
,可以将其展平:

(u1 ++ u2 ++ u3).flatten
这两件事中的一件可能就是你想要的

现在,对于“随机”切换,它没有任何随机性。在每种情况下,都有两个用于理解,并且结果集合的实际实现依赖于原始集合的实现。因此,让我们考虑:

  • u1:外部类型派生自
    范围
    ,内部类型派生自
    字符串
    (要交叉的第一个参数)
  • u2:外部类型派生自
    字符串
    ,内部类型派生自
    列表
    (要交叉的第一个参数)
  • u3:外部类型派生自
    列表
    ,内部类型派生自
    字符串
    (要交叉的第一个参数)

因此,可以很容易地推断,对于
字符串
包装
)和
范围
的理解会导致
向量
,而对于
列表
的理解会导致
列表

,但它没有回答为什么相同的代码在内部运行的问题()方法编译并运行良好。感谢您的详细解释。我昨天更多地关注了代码,所有这些都开始变得有意义了。谢谢。现在变得非常有意义了。
u1 ++ u2 ++ u3
(u1 ++ u2 ++ u3).flatten