首页 > 解决方案 > Scala:不可参数化提取器的解决方法

问题描述

由于提取器不能采用自定义参数(如Stack Overflow 中的回答:可以自定义提取器...),我尝试找到解决以下问题的替代方法。

我有很多可以组合的翻译。在我的代码片段中,维度可以与因子结合。例如"width multiplied by 2". 但它也可以是"width"(不相乘的)。而且还会有更多类似的案例。我尝试使用模式匹配对这些字符串输入进行分类。"width""width multiplied by x"应归类为“宽度”(key "w"),"height""height multiplied by x"归类为“高度”(key "h"),以此类推。

这应该由match以下示例代码片段中的最后一个完成,该示例代码片段将包含许多案例(示例代码片段中的 6 个),每个案例都应带有一个key: String参数("w", "h", "l", "r", "t", "b")。

我试图实现的是将密钥(即"w", "h", "l", "r", "t","b"等)传递给 case Untranslation(v)。但显然我不能这样做(该unapply函数可以采用隐式参数,但没有额外的显式参数)。

现在我尝试找到一种替代但仍然简洁的方式来对我的字符串输入进行分类。

implicit val translations = Map(
  "w" -> "width",
  "h" -> "height",
  "l" -> "left",
  "r" -> "right",
  "t" -> "top",
  "b" -> "bottom",
  // + some more translations
  "m" -> "multiplied by"
)

sealed trait CommandType
object CommandType {
  case object Unmodified extends CommandType
  case object Multiplied extends CommandType
  // ...
}

object Untranslation {
  def unapply(s: String)(implicit t: Map[String, String]): Option[CommandType] = {
    val key: String = "w" // should be variable by case
    val a: List[String] = t(key).split(" ").toList
    val b: List[String] = t("m").split(" ").toList
    val ab: List[String] = a ++ b
    s.split(" ").toList match {
      case `a` => Some(CommandType.Unmodified)
      case `ab` :+ value => Some(CommandType.Multiplied)
      // + some more cases
      case _ => None
    }
  }
}

"width multiplied by 2" match {
  case Untranslation(v) => println(v) // here I would like to pass the key ("w"/"h"/"l"/...)
  case _ => println("nothing found")
}
// outputs: Multiplied

标签: scalapattern-matchingextractorunapply

解决方案


您可以轻松地为提取器创建参数化class而不是object

class Untranslation(val key: String) {
  def unapply(s: String)(implicit t: Map[String, String]): Option[CommandType] = {
    val a: List[String] = t(key).split(" ").toList
    val b: List[String] = t("m").split(" ").toList
    val ab: List[String] = a ++ b
    s.split(" ").toList match {
      case `a` => Some(CommandType.Unmodified)
      case `ab` :+ value => Some(CommandType.Multiplied)
      // + some more cases
      case _ => None
    }
  }
}

To match,提取器需要有一个稳定的标识符,这可以通过将其分配给 a 来完成val(因此不幸的是,每个键都需要额外的一行,但它们当然可以在多个匹配中使用):

val UntranslationW = new Untranslation("w")
val UntranslationT = new Untranslation("t")
...

"width multiplied by 2" match {
  case UntranslationW(v) => ...
  case UntranslationT(v) => ...
  case _ => println("nothing found")
}

推荐阅读