为什么 fold 推断 Any?

问题描述

我正在使用 Scala 2.11.12 和此声明进行编译:

import scala.collection.mutable.Stack
def stackToInt(stack: Stack[Int]): Int =
  stack.zipwithIndex.fold(0){ case (z: Int,(piece: Int,index: Int)) =>
    piece match {
      case 1 => (1 << index) + z
      case _ => z
    }
  }

给出:

stack.zipwithIndex.fold(0){ case (z: Int,index: Int)) =>
                       ^
error: type mismatch;
        found   : Any
        required: Int

在这个项目中编写折叠时我已经处理过很多次这样的东西(第一个在 Scala 中),我总是找到一种不同的方法来使它工作,但也许如果我理解它,我将不再碰这堵墙.

解决方法

因为您需要使用 foldLeft 而不是折叠:

import scala.collection.mutable.Stack
def stackToInt(stack: Stack[Int]): Int =
  stack.zipWithIndex.foldLeft(0) { case (z: Int,(piece: Int,index: Int)) =>
    piece match {
      case 1 => (1 << index) + z
      case _ => z
    }
  }

斯凯蒂:https://scastie.scala-lang.org/VzfKqHZ5QJyNPfwpLVGwSw

fold 不起作用,因为它是语义:fold[A1 >: A](z: A1)(op: (A1,A1) => A1): A1 - 因此在您的情况下 zipWithIndex 给出 (Int,Int) 类型而不是预期的 Int,并且内部函数返回Int - 所以在最后推断的类型你会看到 Any - (Int,Int)Int 之间的共同祖先。

并且 foldLeft[Z](zero: Z) 推断出 Int 因为你给了 0

,

fold 方法需要一个关联二元运算作为它的第二个参数,即一些满足 f(_,_) 的运算

f(x,f(y,z)) = f(f(x,y),z)

您的折叠函数 f 结合了 Int(Int,Int)。 它不能是关联的,因为 f(x,z))f(f(x,z) 不可能同时进行类型检查:

  • 如果 f(x,z)) 类型检查,则 x: Intf(y,z): (Int,Int)
  • 但是,f 的返回类型必须是 (Int,Int)
  • 但是,f(f(x,z) 无法进行类型检查,因为它在第一个参数中得到 (Int,Int),其中需要 Int

因此,在这里谈论结合性没有意义:该声明甚至都不是错误的,它根本无法首先陈述。

由于操作不是关联的,你不能简单地让语言来决定以什么顺序处理元素;你必须选择一个折叠方向,即决定它是否是

f(...f(f(f(a0,x1),x2),x3)...)

(foldLeft)

f(...f(x(n-3),f(x(n-2),f(x(n-1),a0))...)))))

(foldRight)

就您而言,它必须是 foldLeft

类型 Any 被推断为 Int(Int,Int) 之间的最小上限,因为编译器试图将 f 解释为关联操作,其中,必然有两个相同类型的参数。