2016-05-02 4 views
1

Genommen von Apache Spark, Dataset.scala (https://github.com/apache/spark/blob/0c47e274ab8c286498fa002e2c92febcb53905c6/sql/core/src/main/scala/org/apache/spark/sql/Dataset.scala)Was passiert in diesem Scala-Code? Callback-Funktionen

Linie 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) 
} 

Linie 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 
    } 
} 

Was ist hier los? Ich verstehe nicht, wie count() irgendwie mit Callback gleich ist und einen Körper hat; irgendwie wird es auf dem von withCallback zurückgegebenen Datenrahmen aufgerufen, aber ich verstehe die Syntax nicht.

+0

Dies ist [Scala Currying] (http://docs.scala-lang.org/tutorials/tour/currying.html) – zsxwing

Antwort

3

Die count() Methode hat eigentlich keinen eigenen Körper. Was aussieht wie der Körper der count() ist wirklich eine Funktion Literal, die das Argument 'Aktion' von withCallback definiert. count() selbst ist streng genommen nur ein Aufruf an die Methode withCallback(name, df)(action). (Methoden können mehrere Argumentlisten in Scala haben.) Der Wert withCallback ist result, was auch immer die action-Funktion ist.

Allerdings ist die "Verwirrung", die Sie erleben, absichtlich. Dieses Idiom - eine Methode, die eine Terminal-Argumentliste hat, deren Typ entweder eine Funktion oder ein by-name-Wert ist - ermöglicht es, syntaktische Spracherweiterungen zu definieren. Wir sind auf Sprachen gewöhnt, die besondere Syntax wie ...

try { 
    // your code here 
} 

In Scala, können Sie Ihre eigene Funktion schreiben können wie ...

// 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 
} 

..., dass die Nutzer wie

nennen könnte
unreliably { 
    // your code here 
} 

Es sieht genauso aus wie neue Sprachsyntax! Um es wie Ihre motivierende Beispiel könnten wir die Definition ändern, um Argumentlisten zu haben ...

// 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 
} 

Nun konnten wir die Funktion als nennen ...

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

... und es würde eine 90% ige Chance geben, dass dein Code ausgeführt wird. Der Code definiert einen Ausdruck, nicht nur einige valueless Aussage, so könnte man auch schreiben

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

resultOption[String] vom Typ sein würde, so dass Sie folgen könnte, dass mit ...

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

nun über deine eigene kleine "Spracherweiterung" (das ist wirklich nur eine lustige Art, eine Funktion zu nennen), du hast ein nettes kleines Programm, das dich in neun von zehn Tagen glücklich macht.

+0

@ luigi-plinge danke für die Bearbeitung meiner Peinlichkeiten! –

Verwandte Themen