是否可以在Scala中将部分应用的函数参数作为vararg?

时间:2017-12-20 20:50:35

标签: scala

首先是一些代码来说明我的问题

trait Condition extends Serializable {
    def column: String
    def value(row: Row): String = row.getAs[String](column)
    def isTrue(row: Row): Boolean
}

Trait Condition允许定义应用于Row类的任意条件(Spark Row类,以防任何人想知道)。这个特征可以由具体的条件类继承,以模拟一个想要针对特定​​行检查的不同条件,例如:

case class List(values: String*)(val column: String) extends Condition {
    override def isTrue(row: Row): Boolean = values.contains(value(row))
}
case class IsInteger()(val column: String) extends Converter(Long.parseLong)

val condition = List("1", "A")("code")
val isCodeValid = condition.isTrue(row) // assuming the row has a column called "code"

现在我希望能够通过复合条件类组合多个条件:

case class And(conditions: Condition*)(val column: String) extends Condition {
    override def isTrue(row: Row): Boolean = conditions.forall(_.isTrue(row))
}

这允许我写出这样的条件:

val condition = And(List("1", "A")("id"), IsInteger()("id"))("id")
但是,

,这会为每个条件重复列名称。

我更喜欢的是And(List("1", "A"), IsInteger())("id"),其中And条件会将其列名应用于所有基本条件 - 但是,我正在努力学习语法。

基本上,我必须为And定义一个重载的构造函数,而不是采用vararg Condition*参数,而是采用部分应用的Condition vararg班;类似于

的东西
def this(partials: String => Condition*) = this(partials(column): _*)

这个特殊的语法无法编译,我不确定应该是什么样的语法,或者甚至是可能的。

编辑:根据多个建议,重载的构造函数看起来像

def this(partials: (String => Condition)*)(column: String) = 
    this(partials.map(_(column)):_*)(column)

然而,这给了我一个错误

Error:(9, 6) double definition: 
constructor And: (conditions: Condition*)(column: String)And at line 8 
and constructor And: (partials: String => Condition*)(column: String)And at line 9 
have same type after erasure: (conditions: Seq, column: String)And
def this(partials: (String => Condition)*)(column: String) = 
    this(partials.map(_(column)):_*)(column)

我还尝试了相同的重载构造函数而没有列curry,但是后来它抱怨缺少列参数(不出意外,事后看来):

def this(partials: (String => Condition)*) = this(partials.map(_(column)):_*)

Error:(9, 67) not found: value column   
def this(partials: (String => Condition)*) = this(partials.map(_(column)):_*)

1 个答案:

答案 0 :(得分:1)

立即想到的是,并非每个Condition都在一列上。例如。考虑"所有列都相同"或不同列上的两个条件And。所以我们可以稍微改变设计:

trait Condition extends Serializable {
    def isTrue(row: Row): Boolean
}

trait SingleColumnCondition extends Serializable {
    def value(row: Row, column: String): String = row.getAs[String](column)
    def apply(column: String): Condition
}

然后:

case class List(values: String*) extends SingleColumnCondition {
    // requires Scala 2.12 to define a Condition by a lambda
    override def apply(column: String) = row => values.contains(value(row, column))
}

case class And(conditions: Condition*) extends Condition {
    override def isTrue(row: Row): Boolean = conditions.forall(_.isTrue(row))
}

case class SingleColumnAnd(conditions: SingleColumnCondition*) extends SingleColumnCondition {
    override def apply(column: String) = And(conditions.map(_(column))
}

您也可以使用DummyImplicit重载And.apply(当然也可以使用当前方法中的构造函数):

object And {
    def apply(conditions: SingleColumnCondition*)(implicit d: DummyImplicit): SingleColumnCondition =
        SingleColumnAnd(conditions: _*)
}