我环顾四周,发现了其他几个例子,但我并不真正了解这些答案究竟发生了什么。
我想了解为什么以下代码无法编译:
val df = readFiles(sqlContext).
withColumn("timestamp", udf(UDFs.parseDate _)($"timestamp"))
给出错误:
Error:(29, 58) not enough arguments for method udf: (implicit evidence$2: reflect.runtime.universe.TypeTag[java.sql.Date], implicit evidence$3: reflect.runtime.universe.TypeTag[String])org.apache.spark.sql.UserDefinedFunction.
Unspecified value parameter evidence$3.
withColumn("timestamp", udf(UDFs.parseDate _)($"timestamp")).
^
此代码 编译:
val parseDate = udf(UDFs.parseDate _)
val df = readFiles(sqlContext).
withColumn("timestamp", parseDate($"timestamp"))
显然我找到了“解决方法”,但我真的很想理解:
TypeTag
和ClassTag
找到的信息真的很难理解。我不是来自Java背景,这可能没有帮助,但我想我应该能够掌握它...... 答案 0 :(得分:6)
错误消息确实有点误导;原因是函数udf
采用隐式参数列表,但您传递的是实际参数。由于我不太了解spark,因为udf
签名有点复杂,我会尝试用简化的例子来解释发生了什么。
实际上udf
是一个给出一些显式参数的函数,一个隐式参数列表为你提供了另一个函数;让我们定义以下函数,给定pivot
类型为T
的{{1}},我们有一个隐式Ordering
将作为一个函数,允许我们将一个序列拆分为两个,一个包含小于pivot
的元素,另一个包含更大的元素:
def buildFn[T](pivot: T)(implicit ev: Ordering[T]): Seq[T] => (Seq[T], Seq[T]) = ???
让我们忽略实施,因为它并不重要。现在,如果我执行以下操作:
val elements: Seq[Int] = ???
val (small, big) = buildFn(10)(elements)
我会在你的代码中犯同样的错误,即编译器会认为我明确地将elements
作为隐式参数列表传递,这不会编译。我的示例的错误消息将与您拥有的错误消息有所不同,因为在我的情况下,我错误地为隐式参数列表传递的参数数量与预期参数列表匹配,然后错误将是关于不排队的类型。
相反,如果我把它写成:
val elements: Seq[Int] = ???
val fn = buildFn(10)
val (small, big) = fn(elements)
在这种情况下,编译器将正确地将隐式参数传递给函数。我不知道有什么办法可以绕过这个问题,除非你想明确地传递实际的隐含参数,但我觉得它很丑陋而且并不总是实用的;作为参考,这就是我的意思:
val elements: Seq[Int] = ???
val (small, big) = buildFn(10)(implicitly[Ordering[Int]])(elements)