【问题标题】:Is it possible to have a partially applied function parameter as a vararg in Scala?是否可以在 Scala 中将部分应用的函数参数作为可变参数?
【发布时间】:2017-12-20 20:50:34
【问题描述】:

一些代码,以说明我的问题

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 定义一个重载的构造函数,而不是采用可变参数Condition* 参数,而是采用部分应用的Condition 类的可变参数;类似于

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)

我也尝试过相同的重载构造函数,但没有列柯里化,但它抱怨缺少列参数(事后看来,这并不意外):

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)):_*)

【问题讨论】:

  • partials: String => Condition* 不是有效的可变参数,但 partials: (String => Condition)* 是。或者这不是你想要的?
  • 另外partials(column):_*partials.map { _(column) }:_* 没有意义(有点)是...
  • 正如我所说,我什至不确定我的所有语法是否正确,感谢您的建议。我会用我的结果更新问题(仍然没有工作)
  • 您不必使用相同名称的可变参数函数(或两个可变参数构造器),因为它们最终都具有相同的签名。您可以通过创建一个伴生对象并在那里定义一个方法来解决这个问题,该方法将调用另一个构造函数。
  • 谢谢,我回到办公室试试这个

标签: scala


【解决方案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: _*)
}

【讨论】:

  • 我的想法是将每个条件附加到一个列,即使其他列参与检查(即,“如果类型为 S,代码必须为 L”条件仍然属于 - 并最终吐出错误 - “代码”,即使“类型”也参与)。这就是为什么我理想地希望保留这两个构造函数,以便灵活地为相同或不同的列创建 And。 DummyImplicit 的建议非常完美,正是我所需要的。顺便说一句,尽管看起来像是一个纯粹的思想练习,但这实际上是一个真正的银行项目:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多