如何在Scala中使用过滤条件解析字符串并使用它来过滤对象 [英] How to parse a string with filter criteria in scala and use it to filter objects

查看:482
本文介绍了如何在Scala中使用过滤条件解析字符串并使用它来过滤对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比方说,我有一个对象数组,其中包含一些String,Integer和Enum值.并且还包含这些类型的数组和返回这些类型的方法.

Let's say I have an array of objects that contains some String, Integer and Enum values. And also contains arrays of these types and methods that return these types.

例如包含以下ExampleObject的数组:

For example an array containing the following ExampleObject:

object WeekDay extends Enumeration { type WeekDay = Value; val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value  }

class ExampleObject (val integerValue1 : Integer, val integerValue2 : Integer, val stringValue1 : String, val weekDay: WeekDay.Value, val integerArray : Array[Integer])
{  def intReturningMethod1()= {0}  }  

从命令行中,我将带有过滤条件的字符串传递到scala应用程序.例如:

From the command line I pass in a string with filter criteria to the scala application. For example:

-filter_criteria "((integerValue1 > 100 || integerValue2 < 50) && (stringValue1 == "A" || weekDay != "Mon")) || (integerArray(15) == 1) "

运算符应使用这些类型的值来执行常规if语句中的预期操作.

The operators should do what you expect in a normal if statement with these types of values.

如何解析过滤条件字符串并将其用于从数组中过滤ExampleObjects?

How can I parse the filter criteria string and use it to filter ExampleObjects from an array?

或者我应该从哪里开始阅读以了解如何执行此操作?

Or where should I start reading to find out how to do this?

推荐答案

如果要将输入限制为有限的语言,则可以仅使用Scala核心库轻松地为该语言创建解析器.

If you want to restrict the input to a limited language, you can easily create a parser for that language using only the Scala core library.

对于您的示例的精简版本,我已经这样做

I have done this for a stripped down version of your example

  object WeekDay extends Enumeration {
    type WeekDay = Value; val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
  }

  case class ExampleObject(val integerValue1 : Integer, val stringValue1 : String, val weekDay: WeekDay.Value){
    def intReturningMethod1()= {0}
  }

首先,我使用导入并创建一些帮助器:

First I use an import and create some helpers:

  type FilterCriterion = ExampleObject => Boolean
  type Extractor[T] = ExampleObject => T

  def compare[T <% Ordered[T]](v1 : T, c : String, v2 : T) : Boolean = c match {
    case "<" => v1 < v2
    case ">" => v1 > v2
    case "==" => v1 == v2
  }

  def compareAny(v1: Any, c : String, v2 : Any) : Boolean = (v1,v2) match {
    case (s1: String, s2:String) => compare(s1,c,s2)
    case (i1: Int, i2 : Int) => compare(i1,c,i2)
    case (w1 : WeekDay.WeekDay, w2 : WeekDay.WeekDay) => compare(w1.id, c, w2.id)
    case _ => throw new IllegalArgumentException(s"Cannot compare ${v1.getClass} with ${v2.getClass}")
  }

然后我创建解析器:

  object FilterParser extends JavaTokenParsers {
    def intExtractor : Parser[Extractor[Int]] = wholeNumber ^^ {s => Function.const(s.toInt)_} |
      "intReturningMethod1()" ^^^ {(e : ExampleObject) => e.intReturningMethod1()}  |
      "integerValue1" ^^^ {_.integerValue1}
    def stringExtractor : Parser[Extractor[String]] = stringLiteral ^^ {s => Function.const(s.drop(1).dropRight(1))_} |
      "stringValue1" ^^^ {_.stringValue1}
    def weekDayExtrator : Parser[Extractor[WeekDay.WeekDay]] = stringLiteral ^? {
      case s if WeekDay.values.exists(_.toString == s) => Function.const(WeekDay.withName(s))_
    }
    def extractor : Parser[Extractor[Any]] = intExtractor | stringExtractor | weekDayExtrator

    def compareOp : Parser[FilterCriterion] = (extractor ~ ("<"| "==" | ">") ~ extractor) ^^ {
      case v1 ~ c ~ v2 => (e : ExampleObject) => compareAny(v1(e),c,v2(e))
    }

    def simpleExpression : Parser[FilterCriterion] = "(" ~> expression <~ ")" | compareOp
    def notExpression : Parser[FilterCriterion] = "!" ~> simpleExpression ^^ {(ex) => (e : ExampleObject) => !ex(e)} |
      simpleExpression
    def andExpression : Parser[FilterCriterion] = repsep(notExpression,"&&") ^^ {(exs) => (e : ExampleObject) => exs.foldLeft(true)((b,ex)=> b && ex(e))}
    def orExpression : Parser[FilterCriterion] = repsep(andExpression,"||") ^^ {(exs) => (e : ExampleObject) => exs.foldLeft(false)((b,ex)=> b || ex(e))}
    def expression : Parser[FilterCriterion] = orExpression

    def parseExpressionString(s : String) = parseAll(expression, s)
  }

此解析器获取您的输入字符串,并返回一个将ExampleObject映射为布尔值的函数.使用预定义的帮助器函数和解析器规则中定义的匿名函数,在解析输入字符串时构造一次此测试函数.在构造测试函数时,输入字符串的解释仅执行一次.执行测试功能时,将运行已编译的Scale代码.因此它应该运行得很快.

This parser takes your input string and returns a function that maps an ExampleObject to a boolean value. This test function is constructed once while parsing the input string, using the pre-defined helper functions and the anonymous functions defined in the parser rules. The interpretation of the input string is only done once, while constructing the test function. When you execute the test function, you will run compiled Scale code. So it should run quite fast.

测试功能很安全,因为它不允许用户运行任意Scala代码.它将仅由解析器和预定义的帮助器中提供的部分函数构造.

The test function is safe, because it does not allow the user to run arbitrary Scala code. It will just be constructed from the partial function provided in the parser and the pre-defined helpers.

  val parsedCriterion=FilterParser.parseExpressionString("""((integerValue1 > 100 || integerValue1 < 50) && (stringValue1 == "A"))""")

  List(ExampleObject(1,"A", WeekDay.Mon), ExampleObject(2,"B", WeekDay.Sat), ExampleObject(50,"A", WeekDay.Mon)).filter(parsedCriterion.get)

当您希望解析器在ExampleObject中使用更多功能或更多字段时,可以轻松地自己扩展解析器.

You can easily extend the parser yourself, when you want it to use more functions or more fields in your ExampleObject.

这篇关于如何在Scala中使用过滤条件解析字符串并使用它来过滤对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆