【问题标题】:Dataset filter: eta expansion is not done automatically数据集过滤器:eta 扩展不会自动完成
【发布时间】:2017-08-09 22:15:35
【问题描述】:

如果我有一个简单的 Scala 整数集合,并且我定义了一个简单的 方法 isPositive 以在值大于 0 时返回 true,那么我可以将方法传递给 @987654322集合的@方法,如下例所示

def isPositive(i: Int): Boolean = i > 0

val aList = List(-3, -2, -1, 1, 2, 3)
val newList = aList.filter(isPositive)

> newList: List[Int] = List(1, 2, 3)

据我了解,编译器能够通过eta扩展自动将方法转换为函数实例,然后将这个函数作为参数传递。

但是,如果我对 Spark 数据集做同样的事情:

val aDataset = aList.toDS
val newDataset = aDataset.filter(isPositive)

> error

它因众所周知的“方法缺少参数”错误而失败。为了使它工作,我必须使用“_”将方法显式转换为函数:

val newDataset = aDataset.filter(isPositive _)

> newDataset: org.apache.spark.sql.Dataset[Int] = [value: int]

尽管使用map 它可以按预期工作:

val newDataset = aDataset.map(isPositive)

> newDataset: org.apache.spark.sql.Dataset[Boolean] = [value: boolean]

调查签名,我发现Dataset的过滤器的签名与List的过滤器非常相似:

// Dataset:
def filter(func: T => Boolean): Dataset[T]

// List (Defined in TraversableLike):
def filter(p: A => Boolean): Repr

那么,为什么编译器不对 Dataset 的过滤操作进行 eta 扩展呢?

【问题讨论】:

  • 可能是因为 Scala TraversableLike 中的 filter 没有重载,并且在 Spark 数据集中有很多选项
  • @T.Gawęda 同样代表map,但它对数据集的工作方式与预期相同。这就是我添加地图示例的原因。
  • 你是对的!所以等待真正的答案:)
  • 等等,但地图确实有一个额外的隐式参数,编码器
  • @T.Gawęda 我明白了...... map 的“Java”版本将编码器作为第二个参数,因此编译器只为单参数 map 找到一个选项,因此它是能够进行转换。但是,对于过滤器,它会找到具有单个参数的多个候选者。我会做一些测试来验证是否是这个原因。

标签: scala apache-spark


【解决方案1】:

这是由于重载方法和 ETA 扩展的性质造成的。 Eta-expansion between methods and functions with overloaded methods in Scala 解释了失败的原因。

它的要点如下(强调我的):

当重载时,适用性会受到破坏,因为 没有 预期类型(6.26.3,臭名昭著)。未超载时,6.26.2 适用(eta 扩展),因为参数的类型决定 预期的类型。重载时,arg 是 专门键入的 没有预期的类型,因此 6.26.2 不适用;因此既不 d 的重载变体被认为是适用的。

.....

重载分辨率的候选者由“形状”预先筛选。这 形状测试封装了从未使用 eta-expansion 的直觉 因为 args 是在没有预期类型的​​情况下键入的。这个例子显示 eta-expansion 没有被使用,即使它是“唯一的 表达式类型检查。”

正如@DanielDePaula 所指出的,我们在DataSet.map 中看不到这种效果的原因是因为重载的方法实际上需要一个额外的Encoder[U] 参数:

def map[U : Encoder](func: T => U): Dataset[U] = withTypedPlan {
  MapElements[T, U](func, logicalPlan)
}

def map[U](func: MapFunction[T, U], encoder: Encoder[U]): Dataset[U] = {
  implicit val uEnc = encoder
  withTypedPlan(MapElements[T, U](func, logicalPlan))
}

【讨论】:

  • map 函数与 Java 有类似的接口,但它可以工作。你确定它依赖于 Java 接口吗?
  • 其实我刚刚发现 any 重载会使方法失去“预期类型”,所以即使只有filter(expr: String)filter(f: T => Boolean) 它也不会不行。 (source)
  • @DanieldePaula 你说得对,stackoverflow.com/questions/17324247/…。我会更新答案。
  • 对于地图,问题不会发生,因为没有“真正的”重载,因为替代方法有两个参数(函数和编码器)
  • 是的,所以它不依赖于Java接口,而只依赖于重载:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
  • 2012-02-09
  • 2017-05-18
  • 2013-11-06
相关资源
最近更新 更多