【问题标题】:Why does the absence of an else block translate to Unit type return for a function?为什么没有 else 块会转换为函数的 Unit 类型返回?
【发布时间】:2020-02-04 02:53:03
【问题描述】:

我注意到else if(r1 == 0 || divisors.tail.isEmpty || !divisors.tail.contains(r1)){newAcc} 行中存在类型不匹配。因为我的if ... else if ... 没有else 子句

def euclidianDivision(dividend:Int,divisor:Int):(Int,Int)={
  val quotient = dividend/divisor
  val remainder = dividend%divisor

  (quotient,remainder)
}
def firstExpansion(dividend:Int,divisors:List[Int]):List[(Int,Int)]={
  def firstExpansionIter(dividend:Int,divisors:List[Int], acc:List[(Int,Int)]):List[(Int,Int)]= {
    val div1:Int = divisors.head
    val (q1,r1):(Int,Int) = euclidianDivision(dividend,div1)
    val newAcc:List[(Int,Int)] = acc:::List((div1,q1))
    if (divisors.tail.contains(r1)){
      firstExpansionIter(r1,divisors.tail,newAcc)
    }else if(r1 == 0 || divisors.tail.isEmpty || !divisors.tail.contains(r1)){newAcc}
  }
  firstExpansionIter(dividend,divisors,List((0,0))).tail
}

这是错误代码:

错误:(32, 15) 类型不匹配;找到:所需单位:List[(Int, 诠释)] }else if(r1 == 0 || divisors.tail.isEmpty || !divisors.tail.contains(r1)){newAcc}

我可以通过添加else 子句来纠正这个问题,但是如果默认情况下没有处理结果,该函数会尝试返回Unit 怎么办?

注意:更正的代码:

def firstExpansion(dividend:Int,divisors:List[Int]):List[(Int,Int)]={
  def firstExpansionIter(dividend:Int,divisors:List[Int], acc:List[(Int,Int)]):List[(Int,Int)]= {
    val div1:Int = divisors.head
    val (q1,r1):(Int,Int) = euclidianDivision(dividend,div1)
    val newAcc:List[(Int,Int)] = acc:::List((div1,q1))
    if (divisors.tail.contains(r1)){
      firstExpansionIter(r1,divisors.tail,newAcc)
    }else if(r1 == 0 || divisors.tail.isEmpty || !divisors.tail.contains(r1)){newAcc}
    else throw new RuntimeException("Something unexpected happened.")
  }
  firstExpansionIter(dividend,divisors,List((0,0))).tail
}

【问题讨论】:

    标签: scala recursion functional-programming tail-recursion


    【解决方案1】:

    我可以通过添加else 子句来纠正这个问题,但是如果默认情况下没有处理任何结果,函数会尝试返回Unit 怎么办?

    在 Scala 中,与更多“命令式”语言不同,(几乎)一切都是表达式(语句很少),每个表达式的计算结果都是一个值(这也意味着每个方法都返回一个值)。

    这意味着,例如,条件表达式 if (condition) consequence else differentConsequence 是一个计算值的表达式。

    例如,在这段代码中:

    val foo = if (someRandomCondition) 42 else "Hello"
    

    表达式的then 部分将计算为42,表达式的else 部分将计算为"Hello",这意味着if 表达式作为一个整体将计算为42"Hello"

    那么,foo 的类型是什么?好吧,在then 的情况下,值的类型是Int,而在else 的情况下,值的类型是String。但是,这取决于someRandomCondition运行时值,这在编译时是未知的。因此,作为 whole if 表达式类型的唯一选择是 IntString 的最低共同祖先(技术上是 weak least upper bound),即 @987654343 @。

    在具有联合类型的语言中,我们可以给它一个更精确的类型,即联合类型Int | String。 (Scala 3 has union types,所以我们可以给表达式这个精确的类型,尽管 Scala 3 不会推断联合类型。)在 Scala 3 中,我们甚至可以用更精确的类型 42 | "Hello" 来注释它,这实际上是TypeScript 将推断出等价的条件表达式:

    const foo = someRandomCondition ? 42 : "Hello"
    

    现在,让我们继续讨论问题中的代码:

    val bar = if (someRandomCondition) 42
    

    bar 的类型是什么?我们上面说过它是thenelse 分支类型的最低共同祖先,但是……else 分支的类型是什么? else 分支的评估结果是什么?

    请记住,我们说过每个表达式都会计算出一个值,因此else 分支必须计算出某个值。它不能仅仅评估为“无”。

    这可以通过 unit 类型 的所谓 unit value 来解决。单位值和类型被称为“单位”值和类型,因为类型的设计方式使得它只能被单个值占据。单元类型没有成员,没有属性,没有字段,没有语义,什么都没有。因此,不可能区分单元类型的两个值,或者换一种说法:单元类型只能有一个值,因为单元类型的其他值必须完全相同。

    在许多编程语言中,单位值和类型使用与元组值和类型相同的表示法,并简单地用空元组() 标识。一个空元组和一个单元值是一回事:它们没有内容,没有意义。例如,在 Haskell 中,类型和值都写为 ()

    Scala也有单位值,也写成()。然而,单位类型是scala.Unit

    所以,单位值是一个无用的值,用来表示一个无意义的返回值。

    在某些命令式语言中,一个相关但不同的概念是 void 类型(或者在某些语言中,它更像是一种“伪类型”)。

    请注意,“不返回”与“不返回”不同,这将在本答案的第二部分变得很重要。

    所以难题的前半部分是:Scala 语言规范说

    if (condition) expression
    

    等价于

    if (condition) expression else ()
    

    这意味着在(隐式)else 情况下,返回类型为Unit,与List[(Int, Int)] 不兼容,因此会出现类型错误。

    但是为什么抛出异常可以解决这个问题呢?

    这将我们带到 second 特殊类型:NothingNothing 是所谓的底部类型,也就是说它是每个类型的子类型Nothing 有任何价值。那么,Nothing 的返回类型意味着什么呢?

    它表示不返回的表达式。我重复上面所说的:这与什么都不返回不同

    只有副作用的方法不返回任何内容,但确实返回。它的返回类型是Unit,它的返回值是()。它没有有意义的返回值。
    具有无限循环或抛出异常的方法根本不返回。它的返回类型是Nothing它没有返回值

    这就是为什么在else 子句中抛出异常可以解决问题:这意味着else 子句的类型是Nothing,因为Nothing每个类型的子类型then 子句的类型是什么 甚至都没有关系,then 子句和 Nothing 的类型的最低公共超类型将 总是then 子句的类型。 (想一想:父亲和他的任何孩子、孙子、曾孙等的最低共同祖先将永远是父亲本人。TT 的任何子类型的最低共同祖先将永远是T。由于Nothing是所有类型的子类型,TNothing的最低共同祖先将始终是T,因为Nothing始终是T的子类型,无论如何T 是。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-06
      • 1970-01-01
      • 1970-01-01
      • 2017-06-20
      • 2023-03-10
      • 1970-01-01
      相关资源
      最近更新 更多