在Scala中,如何使用unapplySeq和List匹配模式匹配,启动模式和varargs参数?

时间:2014-01-14 03:49:02

标签: scala

鉴于此RichFile类及其伴随对象:

  class RichFile(filePath: String) {
    val file = new File(filePath)
  }
  object RichFile {
    def apply(filePath: String) = new RichFile(filePath)
    def unapply(rf: RichFile) = {
      if (rf == null || rf.file.getAbsolutePath().length() == 0)
        None
      else {
        val basename = rf.file.getName()
        val dirname = rf.file.getParent()
        val ei = basename.indexOf(".")
        if (ei >= 0) {
          Some((dirname, basename.substring(0, ei), basename.substring(ei + 1)))
        } else {
          Some((dirname, basename, ""))
        }
      }
    }
    def unapplySeq(rf: RichFile): Option[Seq[String]] = {
      val filePath = rf.file.getAbsolutePath()
      if (filePath.length() == 0)
        None
      else
        Some(filePath.split("/"))
    }
  }

基本上我想将文件路径的所有组件都提取为序列。为什么通配符匹配在以下代码中不起作用?特别是第一个case语句我收到错误star patterns must correspond with varargs parameters

  val l = List(
    RichFile("/abc/def/name.txt"),
    RichFile("/home/cay/name.txt"),
    RichFile("/a/b/c/d/e"))

  l.foreach { f =>
    f match {
      case RichFile(_*) => println((x, y))
      case RichFile(a, b, c) => println((a, b, c))
    }
  }

我也希望匹配它们,就像我们匹配Scala中的列表一样:

  l.foreach { f =>
    f match {
      case a::b::"def"::tail => println((a, tail))
      case RichFile(_*) => println((x, y))
      case RichFile(a, b, c) => println((a, b, c))
    }
  }

如何使用unapplySeq

执行此操作

1 个答案:

答案 0 :(得分:4)

看起来问题只是你同时拥有unapplyunapplySeq,所以当你在case RichFile中只有一个参数时,scala对于哪种不适用你感到困惑正在努力做到。解决此问题的方法是拥有两个对象,一个包含unapply,另一个包含unapplySeq,因此用法是明确的。

class RichFile(filePath: String) {
  val file = new File(filePath)
  override def toString = f"RichFile($filePath)"
}

object RichFile {
  def apply(filePath: String) = new RichFile(filePath)
  def unapply(rf: RichFile) = {
    if (rf == null || rf.file.getAbsolutePath.isEmpty) None
    else {
      val basename = rf.file.getName
      val dirname = rf.file.getParent
      val (name, ext) = basename.split("\\.", 2) match {
        case Array(name, ext) => (name, ext)
        case Array(name) => (name, "")
      }
      Some((dirname, name, ext))
    }
  }
}

object RichFilePath {
  def unapplySeq(rf: RichFile): Option[Seq[String]] = {
    val filePath = rf.file.getAbsolutePath()
    if (filePath.isEmpty) None
    else Some(filePath.split("/"))
  }
}

val l = List(
  RichFile("/abc/def/name.txt"),
  RichFile("/home/cay/name.txt"),
  RichFile("/a/b/c/d/e"),
  RichFile("/y/z"))

l.foreach { f =>
  f match {
    case RichFilePath(a, b, c) => println("RichFilePath -> " + (a, b, c))
    case RichFile(f) => println("RichFile -> " + f)
  }
}

打印:

RichFile -> (/abc/def,name,txt)
RichFile -> (/home/cay,name,txt)
RichFile -> (/a/b/c/d,e,)
RichFilePath -> (,y,z)

关于::语法,您不能使用::,因为它已经定义并且仅适用于列表。此外,您不希望::,因为以:结尾的运算符是右关联的。这对于List匹配是有意义的,因为该项位于左侧,其余位于右侧。对于您的应用程序,我假设您希望匹配的读取方式与目录结构相同:右侧是文件名,左侧是“rest”。因此,您可以为此定义自己的运算符:

object --> {
  def unapply(rf: RichFile): Option[(RichFile, String)] = {
    if (rf.file.getAbsolutePath.isEmpty)
      None
    else {
      val f = rf.file.getAbsoluteFile
      Some((RichFile(f.getParent), f.getName))
    }
  }
}

l.foreach { f =>
  f match {
    case a --> b --> c => println(f"$a\t$b\t$c")
  }
}

打印

RichFile(/abc)  def name.txt
RichFile(/home) cay name.txt
RichFile(/a/b/c)    d   e
RichFile(/) y   z