2017-03-07 48 views
1

在scala中,我有一个返回值的函数列表。执行功能的顺序很重要,因为功能n的参数是功能n-1的输出。因变量的斯卡拉地图

这暗示使用foldLeft,像:

val base: A 
val funcs: Seq[Function[A, A]] 

funcs.foldLeft(base)(x, f) => f(x) 

(详细:类型A实际上是一个Spark数据帧)。

但是,每个函数的结果是互斥的,最后我想要将每个函数的所有结果联合起来。 这暗示使用map,是这样的:

funcs.map(f => f(base)).reduce(_.union(_) 

但这里的每个函数应用于base这不是我想要的。

短:有序功能可变长度列表需要返回相等长度的返回值,其中每个值n-1是为功能n(从base其中n=0开始)输入的列表。这样可以将结果值连接起来。

我该如何做到这一点?

编辑 例如:

case class X(id:Int, value:Int) 
val base = spark.createDataset(Seq(X(1, 1), X(2, 2), X(3, 3), X(4, 4), X(5, 5))).toDF 

def toA = (x: DataFrame) => x.filter('value.mod(2) === 1).withColumn("value", lit("a")) 
def toB = (x: DataFrame) => x.withColumn("value", lit("b")) 

val a = toA(base) 
val remainder = base.join(a, Seq("id"), "leftanti") 
val b = toB(remainder) 

a.union(b) 

+---+-----+ 
| id|value| 
+---+-----+ 
| 1| a| 
| 3| a| 
| 5| a| 
| 2| b| 
| 4| b| 
+---+-----+ 

这应该具有的功能的任意数量的工作(如toAtoB ... toN每次当先前的结果的余数和传入。下一个函数最后一个联合适用于所有结果

回答

1

Seq已经有一个方法scanLeft,这是否超出现成:

funcs.scanLeft(base)((acc, f) => f(acc)).tail 

确保下降的scanLeft结果的第一个元素,如果你不希望base被列入不。


只使用foldLeft有可能太:

funcs.foldLeft((base, List.empty[A])){ case ((x, list), f) => 
    val res = f(x) 
    (res, res :: list) 
}._2.reverse.reduce(_.union(_)) 

或者:

funcs.foldLeft((base, Vector.empty[A])){ case ((x, list), f) => 
    val res = f(x) 
    (res, list :+ res) 
}._2.reduce(_.union(_)) 

关键是要积聚成一个Seqfold内。

例子:

scala> val base = 7 
base: Int = 7 

scala> val funcs: List[Int => Int] = List(_ * 2, _ + 3) 
funcs: List[Int => Int] = List($$Lambda$1772/[email protected], $$Lambda$1773/[email protected]) 

scala> funcs.foldLeft((base, Vector.empty[Int])){ case ((x, list), f) => 
    | val res = f(x) 
    | (res, list :+ res) 
    | }._2 
res8: scala.collection.immutable.Vector[Int] = Vector(14, 17) 

scala> .reduce(_ + _) 
res9: Int = 31 
+0

我添加了一个例子来让我的问题更加清晰。 – Tim

+0

你的第一个例子正是我所需要的。好的想法积累到列表中,从来没有想过你可以使用foldLeft。真棒:)。 – Tim

0

我已经有了一个使用普通集合的简化解决方案,但是同样的原理适用于

val list: List[Int] = List(1, 2, 3, 4, 5) 
val funcs: Seq[Function[List[Int], List[Int]]] = Seq(times2, by2) 

funcs.foldLeft(list) { case(collection, func) => func(collection) } foreach println // prints 1 2 3 4 5 

def times2(l: List[Int]): List[Int] = l.map(_ * 2) 

def by2(l: List[Int]): List[Int] = l.map(_/2) 

如果您希望单个缩小的值作为最终输出,例如,此解决方案不适用。单个Int;因此它的工作原理如下: F[B] -> F[B] -> F[B]而不是F[B] -> F[B] -> B;虽然我猜这是你需要的。

+0

我添加了一个例子,使我的问题更加清晰。 – Tim