2014-02-05 5 views
0

String, Integer 및 Enum 값이 포함 된 개체 배열이 있다고 가정 해 보겠습니다. 또한 이러한 형식의 배열과 이러한 형식을 반환하는 메서드가 포함되어 있습니다. 예를 들어필터 citeria를 사용하여 문자열을 구문 분석하고 개체를 필터링하는 방법

다음 ExampleObject 함유 배열 : I는 스칼라 애플리케이션 필터 기준 문자열을 전달 명령 행에서

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} } 

. 예를 들면 다음과 같습니다.

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

연산자는 이러한 유형의 값을 사용하여 일반적인 if 문에서 예상 한 것을 수행해야합니다.

필터 조건 문자열을 구문 분석하고이를 사용하여 배열에서 ExampleObjects를 필터링 할 수 있습니까?

아니면 어떻게해야합니까?

+0

예와 같이 임의의 스칼라 문이나 간단한 필터 기준을 전달 하시겠습니까? –

+0

예제와 같이 단순한 필터 기준입니다. – WillamS

+2

임의의 문장을 사용하면 chrisloy의 답변과 같은 일종의 eval 함수를 사용해야합니다. 이것은 당신에게 훌륭한 유연성을 제공합니다. 실행 중에 Scala 컴파일러가 필요하고 거대한 보안 구멍을 열 수 있습니다. 간단한 기준을 사용하여 자신 만의 작은 파서를 작성할 수 있습니다. 옵션 중 하나를 선택하면 솔루션에서 큰 차이가납니다. –

답변

2

입력을 제한된 언어로 제한하려는 경우 스칼라 코어 라이브러리 만 사용하여 해당 언어에 대한 파서를 쉽게 만들 수 있습니다.

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(sdrop(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) 
    } 
: 그럼 난 파서를 만들

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 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} 
    } 

최초의 버전을 박탈에 대한 이런 짓을했는지 좀 헬퍼를 가져 오기를 사용하여 작성

이 파서는 입력 문자열을 가져와 ExampleObject를 부울 값에 매핑하는 함수를 반환합니다 . 이 테스트 함수는 사전 정의 된 도우미 함수와 구문 분석기 규칙에 정의 된 익명 함수를 사용하여 입력 문자열을 구문 분석하는 동안 한 번 작성됩니다. 테스트 함수를 생성하는 동안 입력 문자열의 해석은 한 번만 수행됩니다. 테스트 함수를 실행하면 컴파일 된 스케일 코드가 실행됩니다. 그래서 꽤 빨리 돌아 가야합니다.

테스트 기능은 사용자가 임의의 스칼라 코드를 실행할 수 없기 때문에 안전합니다. 파서와 사전 정의 된 헬퍼에서 제공되는 부분 함수로 구성됩니다.

ExampleObject에서 더 많은 기능이나 더 많은 필드를 사용하려는 경우 쉽게 파서를 확장 할 수 있습니다.

+0

대단히 고마워! – WillamS

+0

원본 답변에는 두 가지 사소한 버그가있었습니다. 나는 그들을 편집에서 고쳤다. 이제 코드가 문제없이 작동합니다. 올바른 버전을 사용하도록주의하십시오. –

+0

나는 파서 결합자를 내 프로그램의 나머지 부분과 결합 시켰고 정말 훌륭하고 빠르게 작동한다! intExtractor에 계산기를 추가하고 싶습니다만, ExampleObject에 연산자를 적용하는 방법을 고수했습니다. => Double 함수 http://stackoverflow.com/questions/21680403/scala-parser-combinator-based-calculator-that- 할 수있는 데이터 레코드 – WillamS

1

find here on GitHub 수있는 트위터의 평가 유틸리티 라이브러리를보고 싶지 수도 있습니다. 전달 된 필터링 로직을 사용하려는 지점의 String으로 대체하고 eval 함수에 전달할 수 있습니다.

+0

위험하지 않습니까? 오히려 허용 된 코드를 연산자와 조합하여 객체의 값과 메서드로 제한하려고합니다. – WillamS

+0

예, 꽤 위험합니다! 트위터는이 설정을로드에 사용하도록 설계했습니다. 당신은 명령 줄에 임의의 코드를 전달 언급했다. 나는 확실히 사용자 입력에 그것을 열지 않을 것이다. – chrisloy

+0

불행히도 컴파일러가 작업을 수행 할 수있는 위치에 있지 않다면 필자는 문자열에 대한 파서 (parser)를 직접 써야한다고 생각한다. – chrisloy

관련 문제