【问题标题】:In Scala, how to use unapplySeq with pattern matching like List, start pattern and varargs parameters?在 Scala 中,如何将 unapplySeq 与 List、start pattern 和 varargs 参数等模式匹配一​​起使用?
【发布时间】:2014-01-14 03:49:02
【问题描述】:

鉴于此 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 做到这一点?

【问题讨论】:

    标签: scala


    【解决方案1】:

    看起来问题只是你同时拥有unapplyunapplySeq,所以当你在case RichFile 中只有一个参数时,scala 对你想要执行哪种 unapply 感到困惑。解决这个问题的方法是有两个对象,一个是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 匹配是有意义的,因为项目在左侧,其余的在右侧。对于您的应用程序,我假设您希望匹配以与目录结构相同的方式读取:文件名在右侧,“其余”在左侧。因此,您可以为此定义自己的运算符:

    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
    

    【讨论】:

      猜你喜欢
      • 2018-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-09
      • 1970-01-01
      • 2014-07-01
      • 2015-12-17
      相关资源
      最近更新 更多