这个 scala 代码是怎么回事?回调函数



取自Apache Spark,Dataset.scala(https://github.com/apache/spark/blob/0c47e274ab8c286498fa002e2c92febcb53905c6/sql/core/src/main/scala/org/apache/spark/sql/Dataset.scala)

第2132行:

/**
  * Returns the number of rows in the [[Dataset]].
  * @group action
  * @since 1.6.0
  */
def count(): Long = withCallback("count", groupBy().count()) { df =>
  df.collect(needCallback = false).head.getLong(0)
}

2393行:

/**
  * Wrap a Dataset action to track the QueryExecution and time cost, then report to the
  * user-registered callback functions.
  */
private def withCallback[U](name: String, df: DataFrame)(action: DataFrame => U) = {
  try {
      df.queryExecution.executedPlan.foreach { plan => plan.resetMetrics()
    }
    val start = System.nanoTime()
    val result = action(df)
    val end = System.nanoTime()
    sqlContext.listenerManager.onSuccess(name, df.queryExecution, end - start)
    result
  } catch {
  case e: Exception =>
    sqlContext.listenerManager.onFailure(name, df.queryExecution, e)
    throw e
  }
}

这是怎么回事?我不明白count()在某种程度上既等于withCallback又有一个body;不知怎么的,它是在withCallback返回的数据帧上调用的,但我不理解语法。

count()方法实际上没有自己的主体。看起来像count()的主体实际上是一个函数文字,它定义了withCallback的"action"参数。严格地说,count()本身只是对方法withCallback(name, df)(action)的调用。(方法在Scala中可以有多个参数列表。)withCallback的值是result,这是action函数的求值结果。

然而,你正在经历的"困惑"是故意的。这个习惯用法——一个有一个终端参数列表的方法,其类型是函数或按名称值——允许定义语法上看起来像语言扩展的内容。我们习惯于有特殊语法的语言,比如。。。

try {
  // your code here
}

在Scala中,您可以编写自己的函数,比如。。。

// don't ask me why you would want to do this
def unreliably[T]( operation : =>T ) : Option[T] = {
   if (scala.math.random < 0.1) Some(operation) else None
}

用户可以像一样调用

unreliably {
  // your code here
}

它看起来就像新的语言语法!为了让它更像你的激励性例子,我们可以修改定义,使其具有论点列表。。。

// don't ask me why you would want to do this
def unreliably[T]( probability : Double )( operation : =>T ) : Option[T] = {
   if (scala.math.random < probability) Some(operation) else None
}

现在,我们可以将函数称为…

unreliably( probability = 0.9 ) {
  // your code here
}

并且你的代码有90%的机会被执行。代码定义了一个表达式,而不仅仅是一些无值语句,因此您也可以编写

val result = unreliably( probability = 0.9 ) {
   "great day"
}

result将是Option[String]类型,因此您可以使用。。。

println(s"""It's a ${result.getOrElse("terrible day")}.""")

现在,通过你自己的小"语言扩展"(这真的只是一种调用函数的有趣方式),你有了一个很好的小程序,它让你十有八九都很开心。

最新更新