2014-01-14 2 views
1

Учитывая это RichFile класс и его компаньон объект:В Scala, как использовать unapplySeq с сопоставлением с образцом, например List, start pattern и varargs parameters?

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("https://stackoverflow.com/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?

ответ

4

Похоже, вопрос только, что у вас есть и unapply и unapplySeq, поэтому если у вас есть только один аргумент в case RichFile, Скал запутался о каком виде вы исключить его пытаетесь сделать. Способ решения этого вопроса состоит в том, чтобы иметь два объекта: один с 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("https://stackoverflow.com/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 
+0

Спасибо за отличное объяснение! – tuxdna

Смежные вопросы