2016-05-02 2 views
1

Взятые из Apache Спарк, Dataset.scala (https://github.com/apache/spark/blob/0c47e274ab8c286498fa002e2c92febcb53905c6/sql/core/src/main/scala/org/apache/spark/sql/Dataset.scala)Что происходит в этом коде Scala? Функции обратного вызова

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

Line 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() как-то равно равен с Callback и имеет тело; каким-то образом он вызывается в dataframe, возвращаемом withCallback, но я не понимаю синтаксис.

+0

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

ответ

3

Метод count() на самом деле не имеет собственного тела. То, что выглядит как тело count(), действительно является литералом функции, который определяет аргумент «действие» withCallback. 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")}.""") 

Теперь, с помощью ваше собственное небольшое «расширение языка» (это действительно просто забавный способ назвать функцию), у вас есть небольшая небольшая программа, которая сделает вас счастливыми девять дней из десяти.

+0

@ luigi-plinge благодарит вас за редактирование моих смущений! –