通过字段匹配Scala对象

时间:2019-07-17 10:25:43

标签: scala functional-programming pattern-matching

我可以通过匹配该对象的字段来匹配整个Scala Object吗?

想象一下,我有一个trait Command,其中有一个字段def name: String。我有这个特征的几个对象。 LsCdMkdirEcho等。这些对象中的每一个都有绑定到name的特定字符串。

例如 Mkdir.name = "mkdir", Cd.name = "cd".

现在,我从外部系统收到一个名为input的字符串。我想匹配此字符串以获取这些对象之一。只是我不想有多个case子句,每个对象一个。这是因为,我正在对这些命令的不同子集进行验证。

我的代码如下:

input match {
case o @ (Mkdir.name | Cd.name | Rm.name) => // Some common code to run and return the matched Command (not string)
...
}

问题在于,在每种情况下,我需要知道输入所匹配的是哪个Command的名称。我无法用这段代码知道这一点,因为在这里,我只将一个字符串匹配为一个字符串的并集,从而在这里丢失了与该字符串匹配的对象(命令)的上下文。

所以,最终我的问题是我可以通过在一个字段上进行匹配来匹配对象吗?

2 个答案:

答案 0 :(得分:1)

自定义提取器是这种用例的朋友。

object IsCommand {
  def unapply(input: String) : Option[Command] = input.toLowerCase match {
    case Ls.name => Some(Ls)
    case Mkdir.name => Some(Mkdir)
    case Rm.name => Some(Rm)
    ...
    case _ => None
  }
}

您现在可以按照以下方式进行模式匹配:

"ls" match {
  case IsCommand(command) => ... // command will be the object Ls
}

我不确定是否可以通过不使用宏之类的方法来缩短unapply方法中的代码,但是在任何情况下,您只需编写一次即可。

答案 1 :(得分:0)

作为Astrid提出的解决方案的替代方案,您可以创建CommandExtractor,它将用作参数化提取器,然后创建用于模式匹配的实例:

class CommandExtractor(commands: Command*) {
  def unapply(name: String): Option[Command] = commands.find(_.name == name)
}

val MkdirOrEcho = CommandExtractor(Mkdir, Echo)
val RmOrCdOrPwd = CommandExtractor(Rm, Cd, Pwd)

input match {
   case MkdirOrEcho(c) => ??? // matching Mkdir or echo
   case RmOrCdOrPwd(c) => ??? //matching rm, cd or pwd
}

这样做的缺点是您丢失了有关正在接收哪个命令的信息,但是由于您已经需要在一个分支中匹配多个命令,因此您可能不需要该信息。