【问题标题】:Scala's tuple unwrapping nuanceScala 的元组展开细微差别
【发布时间】:2010-04-28 07:44:44
【问题描述】:

在尝试将元组解包为 val 时,我注意到 scala 中的以下行为:

scala> val (A, B, C) = (1, 2, 3)
<console>:5: error: not found: value A
       val (A, B, C) = (1, 2, 3)
            ^
<console>:5: error: not found: value B
       val (A, B, C) = (1, 2, 3)
               ^
<console>:5: error: not found: value C
       val (A, B, C) = (1, 2, 3)
                  ^

scala> val (u, v, w) = (1, 2, 3)
u: Int = 1
v: Int = 2
w: Int = 3

这是因为 scala 的模式匹配机制自动假定模式中所有以大写字母开头的标识符都是常量,还是由于其他原因?

谢谢!

【问题讨论】:

    标签: scala design-patterns tuples


    【解决方案1】:

    是的,而且情况会变得更糟:

    val (i, j) : (Int, Int) = "Hello" -> "World"
    

    上面将在运行时编译并失败,并带有ClassCastException。很容易忘记(i, j) 声明是一个模式

    EDIT:对于 ziggystar,Scala 赋值规则在语句中声明:

    val p = expr //or var
    

    p 可以是标识符或模式(请参阅Scala 编程第 15.7 节,pp284)。因此,例如,以下内容是有效的:

    val x :: y :: z :: rest = List(1, 2, 3, 4)
    

    加上模式被删除(即参数类型信息未选中)的事实意味着我的原始示例将编译。

    【讨论】:

    • 这很有趣。但我不明白你在这里所说的模式是什么意思。你能再解释一下吗?
    • 有趣的错误......也许你应该指出它失败了,因为这相当于Tuple2[Int, Int],它被类型擦除为Tuple2[Any, Any]
    【解决方案2】:

    来自[scala] Question about naming conventions你可以阅读

    模式匹配时首字母大写有优势。带有首字母大写字母的标识符被认为是要匹配的值,而不是要绑定的变量。

    【讨论】:

      【解决方案3】:

      如果您需要初始化大量常量并希望避免为每个常量写入 val = 或者您正在达到元组大小限制 (22),则存在一种解决方法。

      来自The Scala Language Specification 的第 4.1 节:

      值定义 val x: T = e 将 x 定义为值的名称 e 的评估结果。值定义 val p1, ..., pn = e 是值定义序列的简写 val p1 = e; ...; val pn = e。

      根据规范,可以通过在右侧指定按顺序返回每个值的表达式来初始化名称以大写字母开头的值序列。

      val iter = Iterator(1, 2, 3)
      val A, B, C = iter.next()
      

      另一个例子:

      val next = { var n = 0; () => { n = n + 1; n } }
      val A, B, C, D, E, F, G, H = next()
      

      在上面这些琐碎的情况下,这种方法不是很有用。下面是一个更有用的示例,它为棋盘的 64 个方格中的每一个初始化一个常量(参见 Square.scala#L31 获取源代码):

      val squareIter = squares.iterator
      val A1, A2, A3, A4, A5, A6, A7, A8,
        B1, B2, B3, B4, B5, B6, B7, B8,
        C1, C2, C3, C4, C5, C6, C7, C8,
        D1, D2, D3, D4, D5, D6, D7, D8,
        E1, E2, E3, E4, E5, E6, E7, E8,
        F1, F2, F3, F4, F5, F6, F7, F8,
        G1, G2, G3, G4, G5, G6, G7, G8,
        H1, H2, H3, H4, H5, H6, H7, H8 = squareIter.next()
      

      【讨论】:

      • 谢谢。很好的主意。第一个例子可以更短:val A, B, C = (Iterator(1, 2, 3).next _)()
      • 谢谢,但缩短版不等同于第一个示例。表达式 (Iterator(1, 2, 3).next _)() 的计算结果为 1,然后将 ABC 设置为 1
      猜你喜欢
      • 1970-01-01
      • 2011-04-26
      • 1970-01-01
      • 1970-01-01
      • 2020-12-13
      • 2011-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多