【问题标题】:Scala list of regular expressions with pattern matching具有模式匹配的正则表达式的 Scala 列表
【发布时间】:2018-05-26 10:41:40
【问题描述】:

我有一些存储在变量中的正则表达式和一个匹配操作,它根据匹配的模式返回一个字符串(如下所示)。我想将这些变量转换成一个数组或一个正则表达式列表,这样它们就可以被索引,我可以得到相应的组并返回适当的结果。

现有代码:

def parseString(str : String) : String = {

    val ptrn2 = """/foo/bar/find/(apple|orange)/(\w+)$""".r
    val ptrn3 = """/foo/bar/(fruit|vegetable)/(banana|carrot)/(\w+)$""".r
    val ptrn4 = """/foo/bar/(volume|size)/(\d+)/(\d+)/(\d+)$""".r
    // more variables

    val response =  str match {
        case ptrn2(a,b) => "/foo/bar/"+ a +"/{id}"
        case ptrn3(a,b,c) => "/foo/bar/"+ a +"/" + b + "/{ref}"
        case ptrn4(a,b,c,d) => "/foo/bar/"+ a +"/" + (b.toInt*c.toInt*d.toInt)
        // more case statements
        case _ => str
     }
     return response 
}

我尝试使用以下语法访问特定索引,传递变量来获取组,但这是不正确的。这有什么问题?

 val prtn : List[Regex] = List(new Regex("regex here"),
  new Regex("regex2 here"),
  new Regex("regex3 here"))

 val response =  str match {
        case ptrn(0)(a,b) => "/foo/bar/"+ a +"/{id}"
        case ptrn(1)(a,b,c) => "/foo/bar/"+ a +"/" + b + "/{ref}"
        case ptrn(2)(a,b,c,d) => "/foo/bar/"+ a +"/" + (b.toInt*c.toInt*d.toInt)
        case _ => str
 }

必须有一种方法可以通过匹配块中的 Arrays/List 访问它,或者如果 Map 返回适当的结果会更好。有谁知道如何在 Scala 中解决这个问题?

【问题讨论】:

    标签: regex scala list pattern-matching


    【解决方案1】:

    我现在没有时间测试,所以也许有人会给你一个更准确的答案。但我认为这与 scala 与列表进行模式匹配的方式有关:通常它与unapply 相关,而使用ptrn(0) 你会创建一个apply。请尝试:

    val response =  str match {
        case p(a,b) if p == ptrn(0) => "/foo/bar/"+ a +"/{id}"
        case p(a,b,c) if p == ptrn(1) => "/foo/bar/"+ a +"/" + b + "/{ref}"
        case p(a,b,c,d) if p == ptrn(2) => "/foo/bar/"+ a +"/" + (b.toInt*c.toInt*d.toInt)
        case _ => str
    }
    

    【讨论】:

    • 这不会编译,因为p 无法解析。要与提取器一起使用,p 必须是“稳定标识符”(例如对值的引用)。
    【解决方案2】:

    Scala 的模式匹配要求匹配的表达式是稳定标识符(参见定义here)。在第一种情况下,正则表达式是一个变量,每个模式都是一个稳定的标识符。但列表中的元素不是。

    我不认为你可以通过模式匹配来实现这一点,你将不得不求助于Regex 的不涉及unapply 的 API。此外,您还可以通过创建一个不仅包含正则表达式,而且还包含每个正则表达式要做什么的列表来减少冗长。

    这是一种可能的实现方式:

    // instead of a simple list of regular expressions, make this a list of Tuples of (regex, builder),
    // where the builder is a function from the matched groups (List[String]) to the desired result (String)
    val ptrn = List(
      (
        """/foo/bar/find/(apple|orange)/(\w+)$""".r, 
        (groups: List[String]) => s"/foo/bar/${groups.head}/{id}"
      ),
      (
        """/foo/bar/(fruit|vegetable)/(banana|carrot)/(\w+)$""".r, 
        (groups: List[String]) => s"/foo/bar/${groups.head}/${groups(1)}/{ref}"
      ),
      (
        """/foo/bar/(volume|size)/(\d+)/(\d+)/(\d+)$""".r, 
        (groups: List[String]) => s"/foo/bar/${groups.head}/${groups(1).toInt * groups(2).toInt * groups(3).toInt})"
      )
    )
    
    // for some input:
    val str = "/foo/bar/fruit/banana/split"
    
    // First, flatMap to tuples of (Regex.Match, builder) - 
    // the flatMap will "filter out" the ons that didn't match because None results would be lost 
    val res = ptrn.flatMap {
      case (reg, builder) => reg.findFirstMatchIn(str).map(m => (m, builder))
    }.headOption.map { // then, take the first match and apply the builders to the matched groups
      case (m, builder) => builder.apply(m.subgroups)
    }.getOrElse(str)   // if no match found, use the original String
    
    println(res) // prints /foo/bar/fruit/banana/{ref} 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-01
      • 2013-01-23
      • 2019-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-04
      • 1970-01-01
      相关资源
      最近更新 更多