我有一个类,其所有函数都具有相同的arity和相同类型的输出。 (为什么?每个函数都是一个单独的处理器,应用于Spark DataFrame并生成另一个DataFrame)。
所以,这个课程看起来像这样:
class Processors {
def p1(df: DataFrame): DataFrame {...}
def p2(df: DataFrame): DataFrame {...}
def p3(df: DataFrame): DataFrame {...}
...
}
然后我通过映射Processors.getClass.getMethod
来应用给定DataFrame的所有方法,这允许我添加更多处理器而不更改代码中的任何其他内容。
我想要做的是为Processors
下的方法定义一个模板,该模板会限制所有方法只接受一个DataFrame并返回一个DataFrame。有没有办法做到这一点?
答案 0 :(得分:1)
这是你的意思吗?
class Processors {
type Template = DataFrame => DataFrame
val p1: Template = ...
val p2: Template = ...
val p3: Template = ...
def applyAll(df: DataFrame): DataFrame =
p1(p2(p3(df)))
}
答案 1 :(得分:1)
对可以添加到"列表中的哪种功能实施限制"可以通过使用适当的容器类而不是泛型类来保存受限制的方法。然后,受限方法的容器可以是某个新类或对象的一部分或主程序的一部分。
通过使用容器(例如带有字符串键和限制值的Map)来保存特定类型的函数,您在下面失去的是编译时检查方法名称。例如叫三联vs trilpe
使用Function1 from the scala standard library可以将函数限制为采用类型T并返回相同类型T的type F[T]
。 Function1[A,B]
允许任何具有输入类型A和输出类型B的单参数函数,但我们希望这些输入/输出类型相同,因此:
type F[T] = Function1[T,T]
对于容器,我将证明scala.collection.mutable.ListMap[String,F[T]]
假设以下要求:
虽然您可以选择其他一些可变或不可变的集合类(例如Vector[F[T]]
,如果您只想对方法进行编号),并且仍然受益于未来开发人员可以将哪种函数包含到容器中。< / p>
抽象类型可以定义为:
type TaskMap[T] = ListMap[String, F[T]]
对于您的特定应用程序,您可以将其实例化为:
val Processors:TaskMap[Dataframe] = ListMap(
"p1" -> ((df: DataFrame) => {...code for p1 goes here...}),
"p2" -> ((df: DataFrame) => {...code for p2 goes here...}),
"p3" -> ((df: DataFrame) => {...code for p3 goes here...})
)
然后调用您使用的其中一个函数
Processors("p2")(someDF)
为了简化演示,让我们暂时忘记Dataframes并考虑这个方案是否适用于整数。
考虑下面的简短程序。该系列&#34; myTasks&#34;只能包含从Int到Int的函数。下面的所有行都在scala解释器v2.11.6中进行了测试,因此您可以逐行跟踪。
import scala.collection.mutable.ListMap
type F[T] = Function1[T,T]
type TaskMap[T] = ListMap[String, F[T]]
val myTasks: TaskMap[Int] = ListMap(
"negate" -> ((x:Int)=>(-x)),
"triple" -> ((x:Int)=>(3*x))
)
我们可以在容器中添加一个新功能,添加7并命名为&#34; add7&#34;
myTasks += ( "add7" -> ((x:Int)=>(x+7)) )
并且scala解释器回复:
res0: myTasks.type = Map(add7 -> <function1>, negate -> <function1>, triple -> <function1>)
但是我们无法添加一个名为&#34; half&#34;因为它会返回一个Float,而Float不是一个Int,应该触发一个类型错误
myTasks += ( "half" -> ((x:Int)=>(0.5*x)) )
这里我们收到以下错误消息:
scala> myTasks += ( "half" -> ((x:Int)=>(0.5*x)) )
<console>:12: error: type mismatch;
found : Double
required: Int
myTasks += ( "half" -> ((x:Int)=>(0.5*x)) )
^
在编译的应用程序中,这将在编译时找到。
如何调用以这种方式存储的函数对于单个调用来说有点冗长,但可以非常方便。
假设我们要打电话给&#34; triple&#34; 10月。
我们无法写
triple(10)
<console>:9: error: not found: value triple
相反它是
myTasks("triple")(10)
res4: Int = 30
如果您有一个要执行的任务列表但只想允许myTasks中列出的任务,则此表示法变得更有用。
假设我们想要在输入数据&#34; 10&#34;
上运行所有任务 myTasks mapValues { _ apply 10 }
res9: scala.collection.Map[String,Int] =
Map(add7 -> 17, negate -> -10, triple -> 30)
假设我们想要三倍,然后添加7,然后否定
如果单独需要每个结果,如上所述,则变为:
List("triple","add7","negate") map myTasks.apply map { _ apply 10 }
res11: List[Int] = List(30, 17, -10)
但是&#34;三倍,然后加7,然后否定&#34;也可以描述一系列要做的步骤10,即我们想要 - ((3 * 10)+7)&#34;而斯卡拉也可以这样做
val myProgram = List("triple","add7","negate")
myProgram map myTasks.apply reduceLeft { _ andThen _ } apply 10
res12: Int = -37
打开为自己的可自定义任务编写解释器的大门,因为我们也可以编写
val magic = myProgram map myTasks.apply reduceLeft { _ andThen _ }
然后magic
是一个函数,从int到int,它可以采用aribtrary整数或以函数的形式工作。
scala> magic(1)
res14: Int = -10
scala> magic(2)
res15: Int = -13
scala> magic(3)
res16: Int = -16
scala> List(10,20,30) map magic
res17: List[Int] = List(-37, -67, -97)