J'essaie de m'enseigner Scala tout en essayant d'écrire du code qui est idiomatique d'un langage fonctionnel, c'est-à-dire d'écrire un code fonctionnel mieux, plus élégant.

J'ai le code suivant qui fonctionne bien:

import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SparkSession}
import java.time.LocalDate
object DataFrameExtensions_ {
  implicit class DataFrameExtensions(df: DataFrame){
    def featuresGroup1(groupBy: Seq[String], asAt: LocalDate): DataFrame = {df}
    def featuresGroup2(groupBy: Seq[String], asAt: LocalDate): DataFrame = {df}
  }
}
import DataFrameExtensions_._
val spark = SparkSession.builder().config(new SparkConf().setMaster("local[*]")).enableHiveSupport().getOrCreate()
import spark.implicits._
val df = Seq((8, "bat"),(64, "mouse"),(-27, "horse")).toDF("number", "word")
val groupBy = Seq("a","b")
val asAt = LocalDate.now()
val dataFrames = Seq(df.featuresGroup1(groupBy, asAt),df.featuresGroup2(groupBy, asAt))

La dernière ligne me dérange cependant. Les deux fonctions (featuresGroup1, featuresGroup2) ont toutes deux la même signature:

scala> :type df.featuresGroup1(_,_)
(Seq[String], java.time.LocalDate) => org.apache.spark.sql.DataFrame

scala> :type df.featuresGroup2(_,_)
(Seq[String], java.time.LocalDate) => org.apache.spark.sql.DataFrame

Et prendre les mêmes val s comme paramètres donc je suppose que je peux écrire cette ligne d'une manière plus fonctionnelle (peut-être en utilisant .map d'une manière ou d'une autre) ce qui signifie que je peux écrire la liste des paramètres juste une fois et la passer aux deux les fonctions. Je ne peux pas comprendre la syntaxe cependant. J'ai pensé que je pourrais peut-être construire une liste de ces fonctions mais cela ne fonctionne pas:

scala> Seq(featuresGroup1, featuresGroup2)
<console>:23: error: not found: value featuresGroup1
       Seq(featuresGroup1, featuresGroup2)
           ^
<console>:23: error: not found: value featuresGroup2
       Seq(featuresGroup1, featuresGroup2)
                           ^

Quelqu'un peut-il aider?

2
jamiet 23 mai 2018 à 08:35

5 réponses

Meilleure réponse

J'ai pensé que je pourrais peut-être construire une liste de ces fonctions mais cela ne fonctionne pas:

Pourquoi écrivez-vous juste featuresGroup1/2 ici alors que vous aviez déjà la syntaxe correcte df.featuresGroup1(_,_) juste au-dessus?

Seq(df.featuresGroup1(_,_), df.featuresGroup2(_,_)).map(_(groupBy, asAt))

df.featuresGroup1 _ devrait également fonctionner.

df.featuresGroup1 en lui-même fonctionnerait si vous aviez un type attendu, par exemple

val dataframes: Seq[(Seq[String], LocalDate) => DataFrame] = 
  Seq(df.featuresGroup1, df.featuresGroup2)

Mais dans ce cas précis, fournir le type attendu est plus détaillé que d'utiliser des lambdas.

2
Alexey Romanov 23 mai 2018 à 08:49

Pourquoi ne pas simplement créer une fonction dans DataFrameExtensions pour le faire?

def getDataframeGroups(groupBy: Seq[String], asAt: String) = Seq(featuresGroup1(groupBy,asAt), featuresGroup2(groupBy,asAt))
1
RoberMP 23 mai 2018 à 07:02

J'ai pensé que je pourrais peut-être construire une liste de ces fonctions mais cela ne fonctionne pas

Vous devez explicitement exécuter expansion eta pour transformer les méthodes en fonctions (elles ne sont pas les mêmes dans Scala), en utilisant un opérateur de soulignement:

val funcs = Seq(featuresGroup1 _, featuresGroup2 _)

Ou en utilisant des espaces réservés:

val funcs = Seq(featuresGroup1(_, _), featuresGroup2(_, _))

Et vous avez parfaitement raison d'utiliser l'opérateur map:

val dataFrames = funcs.map(f => f(groupBy, asAdt))

Je déconseille fortement d'utiliser des implicites de types String ou Seq, car s'ils sont utilisés à plusieurs endroits, ils conduisent à des bogues subtils qui ne sont pas immédiatement évidents à partir du code et le code sera susceptible de se casser quand il est déplacé quelque part.

Si vous souhaitez utiliser des implicits, enveloppez-les dans des types personnalisés:

case class DfGrouping(groupBy: Seq[String]) extends AnyVal

implicit val grouping: DfGrouping = DfGrouping(Seq("a", "b"))
2
Oleg Pyzhcov 23 mai 2018 à 06:34

J'aime mieux cette réponse, gracieuseté d'Alexey Romanov.

import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SparkSession}
import java.time.LocalDate
object DataFrameExtensions_ {
  implicit class DataFrameExtensions(df: DataFrame){
    def featuresGroup1(groupBy: Seq[String], asAt: LocalDate): DataFrame = {df}
    def featuresGroup2(groupBy: Seq[String], asAt: LocalDate): DataFrame = {df}
  }
}
import DataFrameExtensions_._
val spark = SparkSession.builder().config(new SparkConf().setMaster("local[*]")).enableHiveSupport().getOrCreate()
import spark.implicits._
val df = Seq((8, "bat"),(64, "mouse"),(-27, "horse")).toDF("number", "word")
val groupBy = Seq("a","b")
val asAt = LocalDate.now()
Seq(df.featuresGroup1(_,_), df.featuresGroup2(_,_)).map(_(groupBy, asAt))
0
jamiet 23 mai 2018 à 07:59

Je pense que vous pouvez créer une liste de fonctions comme ci-dessous:

val funcs:List[DataFrame=>(Seq[String], java.time.LocalDate) => org.apache.spark.sql.DataFrame]  = List(_.featuresGroup1, _.featuresGroup1)
funcs.map(x => x(df)(groupBy, asAt))

Il semble que vous ayez une liste de fonctions qui convertissent un DataFrame en un autre DataFrame. Si tel est le cas, vous pouvez aller un peu plus loin avec Endo dans Scalaz

0
Binzi Cao 23 mai 2018 à 06:35