【问题标题】:Why does this code need an empty line or a semicolon?为什么这段代码需要空行或分号?
【发布时间】:2010-09-17 12:35:03
【问题描述】:
case class MyInt(val i : Int) {
    private def factorial(a : Int) : Int = a match {
        case 0 => 1
        case n => (n) * factorial(n-1)
    }
    def ! = factorial(i)
    override def toString = i.toString
}

object MyInt {
    implicit def intToMyInt(x : Int) = MyInt(x)
    implicit def myIntToInt(x : MyInt) = x.i
}
import MyInt._

object Factorial {  
    def main(args: Array[String]): Unit = {
        val a = 5
        val aFact = a!
        println("factorial of " + a + " is " + aFact)

  }
}

如果我没有在println 之前添加分号或空行,则编译失败:

递归值aFact需要类型

【问题讨论】:

  • @Missing Factor:我认为这不是同一个问题。这两个问题都是关于类型推断失败但这里我有一个一元运算符和隐式转换(我不明白你为什么删除类型推断标签)
  • @onof:编译器不知道它是一元运算符。对于编译器来说,这只是一个普通的方法。问题基本相同:“为什么分号推断在后缀表示法中调用此方法失败?”。
  • @onof:这两行的解析如下:val aFact = a.!(println("factorial of " + a + " is " + aFact))
  • @onof:我不知道该怎么解释你。也许这两个代码 sn-ps 会有所帮助:ideone.com/wM13l, ideone.com/4PPx1.

标签: scala


【解决方案1】:

所有这些关于递归函数和类型的讨论都是红鲱鱼。 Scala 的语法不允许在表达式末尾以外的任何其他位置使用后缀运算符。这就是我们所说的语法:没有任何语义的事物的语法。以下是规范中的相关语法:

Expr        ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr
              | Expr1
Expr1       ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] else Expr]
              | ‘while’ ‘(’ Expr ‘)’ {nl} Expr
              | ‘try’ ‘{’ Block ‘}’ [‘catch’ ‘{’ CaseClauses ‘}’]
                [‘finally’ Expr]
              | ‘do’ Expr [semi] ‘while’ ‘(’ Expr ’)’
              | ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’)
              | {nl} [‘yield’] Expr
              | ‘throw’ Expr
              | ‘return’ [Expr]
              | [SimpleExpr ‘.’] id ‘=’ Expr
              | SimpleExpr1 ArgumentExprs ‘=’ Expr
              | PostfixExpr
              | PostfixExpr Ascription
              | PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’
PostfixExpr ::= InfixExpr [id [nl]]

PostfixExpr 出现在这些旁边的仅有两个位置是在 case 语句的 if 之后和参数列表上的 : _* 之前。所以,从这个角度来看,我们看到后缀表达式的方法名称右侧唯一可以出现的东西是类型归属或match

那么,结束语是什么?好吧,表达式出现在语法的很多地方,所以有很多东西可以结束它。在这个特定的示例中,表达式是 Block 中的 BlockStat,因此它必须以分号结尾,这可以推断或不推断。

要推断这个分号,下一行必须不能是可以被解析为另一种表达式的东西。在这种特殊情况下,我们有这样的:

    val aFact = a!
    println("factorial of " + a + " is " + aFact)

现在,让我们从编译器的角度重写它:

    val id = id id
    id ( stringLit id id id stringLit id id )

这些文字和标识符的解析如下:

    val id = id id id ( expr )
    val Pattern2 = SimpleExpr1 id SimpleExpr1 ArgumentExprs
    val Pattern2 = InfixExpr
    val Pattern2 = Expr
    val PatDef
    PatVarDef
    Def
    BlockStat

所以当编译器解析你的程序时,这看起来像是一个有效的中缀表达式。之后,它发现类型不匹配,但已经太迟了,不能再回头看看是否可以推断出分号。

【讨论】:

    【解决方案2】:

    因为否则! 可以被解释为二进制表达式

    a ! println("factorial of " + a + " is " + aFact)
    

    如果编译器评估所涉及的两种类型的表达式,则可以证明相反的情况,因为这些类型有可能进行隐式转换以允许调用。

    但由于正确的操作数涉及aFact本身,该值是递归的,Scala无法确定它的类型,因此不是运算符的正确固定性。

    你需要在这里明确!

    【讨论】:

    • 但是 println 是 Unit,为什么它没有意识到呢!是一元的?而且,为什么空行就足够了?
    • 因为他看不出没有二进制版本。除非您知道类型,否则它是模棱两可的。
    • 这个决定是由解析器在类型已知之前做出的。我最近向 IntelliJ 添加了一个检查,以突出显示像这样的潜在问题,您的代码如下所示:twitpic.com/2p99rl
    • 可能存在从Unit 到定义二进制! 的类型的隐式转换。
    • 这是错误的,错误的,错误的答案。它与打字无关。解析完全在输入之前发生。 Daniel Sobral 的回答是正确的。
    【解决方案3】:

    以下代码编译正常:

    package it.onof.scalaDemo
    
    case class MyInt(val i : Int) {
        private def factorial(a : Int) : Int = a match {
            case 0 => 1
            case n => (n) * factorial(n-1)
        }
        def ! = factorial(i)
        override def toString = i.toString
    }
    
    object MyInt {
        implicit def intToMyInt(x : Int) = MyInt(x)
        implicit def myIntToInt(x : MyInt) = x.i
    }
    import MyInt._
    
    object Factorial {  
        def main(args: Array[String]): Unit = {
            val a = 5
            val aFact:Int = a.!
            println("factorial of " + a + " is " + aFact)
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-15
      • 2013-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-02
      相关资源
      最近更新 更多