복붙노트

[SCALA] 서열에서 만족 조건 X 그 첫 번째 요소를 찾기

SCALA

서열에서 만족 조건 X 그 첫 번째 요소를 찾기

일반적으로, 어떻게 서열의 특정 조건을 만족하는 첫 번째 요소를 찾는 방법은?

예를 들어, 나는 가능한 날짜 형식의 목록을 가지고 있고, 나는 나의 날짜 문자열을 구문 분석 할 수있는 첫 번째 형식의 구문 분석 된 결과를 찾고 싶어요.

val str = "1903 January"
val formats = List("MMM yyyy", "yyyy MMM", "MM yyyy", "MM, yyyy")
  .map(new SimpleDateFormat(_))
formats.flatMap(f => {try {
  Some(f.parse(str))
}catch {
  case e: Throwable => None
}}).head

나쁘지 않다. 그러나 1. 조금 추한. 2. 그것은 ( "MM 일"및 "MM, YYYY"형식 시도) 일부 불필요한 작업을했다. 아마도 더 우아하고 관용적 인 방법은 무엇입니까? (반복자를 사용하고 계십니까?)

해결법

  1. ==============================

    1.있는 거 확신 적어도 하나의 의지 형식이 성공하면 경우 :

    있는 거 확신 적어도 하나의 의지 형식이 성공하면 경우 :

    formats.view.map{format => Try(format.parse(str)).toOption}.filter(_.isDefined).head
    

    당신은 조금 더 안전 할하려는 경우 :

    formats.view.map{format => Try(format.parse(str)).toOption}.find(_.isDefined)
    

    시도는 스칼라 2.10 년에 도입되었다.

    보기는 느리게 값을 계산하는 컬렉션의 유형이다. 정의 된 첫 번째 하나를 찾을 필요가있다 그것은 컬렉션 만 많은 항목에 시도 내에서의 코드를 적용합니다. 첫 번째 형식은 문자열에 적용되는 경우, 다음은 문자열로 나머지 형식을 적용하려고하지 않을 것이다.

  2. ==============================

    2.당신은 순서에 찾기 방법을 사용해야합니다. 일반적으로 당신은 내장 선호한다 방법, 그들은 특정 순서에 최적화 될 수 있기 때문이다.

    당신은 순서에 찾기 방법을 사용해야합니다. 일반적으로 당신은 내장 선호한다 방법, 그들은 특정 순서에 최적화 될 수 있기 때문이다.

    Console println List(1,2,3,4,5).find( _ == 5)
    res: Some(5)
    

    즉 경기 첫하여 SimpleDateFormat을 반환한다 :

     val str = "1903 January"
     val formats = List("MMM yyyy", "yyyy MMM", "MM yyyy", "MM, yyyy")
       .map(new SimpleDateFormat(_))
     formats.find { sdf => 
          sdf.parse(str, new ParsePosition(0)) != null
     }
    
     res: Some(java.text.SimpleDateFormat@ef736ccd)
    

    처리되고있다 먼저 날짜를 반환 :

    val str = "1903 January"
    val formats = List("MMM yyyy", "yyyy MMM", "MM yyyy", "MM, yyyy").map(new SimpleDateFormat(_))
    val result = formats.collectFirst { 
      case sdf if sdf.parse(str, new ParsePosition(0)) != null => sdf.parse(str)
    }
    

    또는 게으른 모음을 사용합니다 :

    val str = "1903 January"
    val formats = List("MMM yyyy", "yyyy MMM", "MM yyyy", "MM, yyyy").map(new SimpleDateFormat(_))
    formats.toStream.flatMap { sdf =>
       Option(sdf.parse(str, new ParsePosition(0)))
    }.headOption
    
    res: Some(Thu Jan 01 00:00:00 EET 1903)
    
  3. ==============================

    3.이것은 불필요한 평가를 방지 할 수 있습니다.

    이것은 불필요한 평가를 방지 할 수 있습니다.

    formats.collectFirst{ case format if Try(format.parse(str)).isSuccess => format.parse(str) } 
    

    해석에있어서의 평가의 수는 시도 + 1의 수이다.

  4. ==============================

    4.스칼라 추출기와 게으름과 같은 버전 :

    스칼라 추출기와 게으름과 같은 버전 :

    case class ParseSpec(dateString: String, formatter:DateTimeFormatter)
    
    
    object Parsed {
      def unapply(parsableDate: ParseSpec): Option[LocalDate] = Try(
        LocalDate.parse(parsableDate.dateString, parsableDate.formatter)
      ).toOption
    }
    
    
    private def parseDate(dateString: String): Option[LocalDate] = {
      formats.view.
        map(ParseSpec(dateString, _)).
         collectFirst  { case Parsed(date: LocalDate) => date }
    }
    
  5. ==============================

    5.그 첫 번째 요소의 일치 조건 (있는 경우)의 옵션을 반환로 바로 찾기 방법을 사용하십시오

    그 첫 번째 요소의 일치 조건 (있는 경우)의 옵션을 반환로 바로 찾기 방법을 사용하십시오

    formats.find(str => Try(format.parse(str)).isSuccess)
    

    또한, 실행은 첫 번째 하나를 따기 전에 세트의 모든 요소를 ​​분석하려고하지 않습니다 그래서, 첫 경기에서 멈 춥니 다. 다음은 그 예이다 :

    def isSuccess(t: Int) = {
      println(s"Testing $t")
      Math.floorMod(t, 3) == 0
    }
    isSuccess: isSuccess[](val t: Int) => Boolean
    
    List(10, 20, 30, 40, 50, 60, 70, 80, 90).filter(isSuccess).headOption
    Testing 10
    Testing 20
    Testing 30
    Testing 40
    Testing 50
    Testing 60
    Testing 70
    Testing 80
    Testing 90
    res1: Option[Int] = Some(30)
    
    Stream(10, 20, 30, 40, 50, 60, 70, 80, 90).filter(isSuccess).headOption
    Testing 10
    Testing 20
    Testing 30
    res2: Option[Int] = Some(30)
    
    List(10, 20, 30, 40, 50, 60, 70, 80, 90).find(isSuccess)
    Testing 10
    Testing 20
    Testing 30
    res0: Option[Int] = Some(30)
    

    스트림 것이 정말 중요하지 않습니다. 당신은 예를 들어 인 IntelliJ를 사용하고 또한 경우에 당신을 제안합니다 :

    seq.filter(p).headOption  
    
    seq.find(p)
    
  6. ==============================

    6.

    scala> def parseOpt(fmt: SimpleDateFormat)(str: String): Option[Date] =
         |   Option(fmt.parse(str, new ParsePosition(0)))
    tryParse: (str: String, fmt: java.text.SimpleDateFormat)Option[java.util.Date]
    
    scala> formats.view.flatMap(parseOpt(fmt)).headOption
    res0: Option[java.util.Date] = Some(Thu Jan 01 00:00:00 GMT 1903)
    

    그런데, SimpleDateFormat에 이후 수단 위의 코드는 스레드로부터 안전하지 않습니다 것을 하나, 비 스레드 안전합니다!

  7. ==============================

    7.나는 꼬리 재귀를 사용하는 것이 훨씬 낫다 가장 효율적인 솔루션은 지금까지 여기에 제공되는 지금까지 생각 :

    나는 꼬리 재귀를 사용하는 것이 훨씬 낫다 가장 효율적인 솔루션은 지금까지 여기에 제공되는 지금까지 생각 :

    implicit class ExtendedIterable[T](iterable: Iterable[T]) {
      def findFirst(predicate: (T) => Boolean): Option[T] = {
        @tailrec
        def findFirstInternal(remainingItems: Iterable[T]): Option[T] = {
          if (remainingItems.nonEmpty)
            if (predicate(remainingItems.head))
              Some(remainingItems.head)
            else
              findFirstInternal(remainingItems.tail)
          else
            None
        }
        findFirstInternal(iterable)
      }
    }
    

    그것은 단순히 당신이 필요한 곳마다 다음과 같은 일을 할 수있는 위의 클래스를 가져 오기에 당신을 허용합니다 :

    formats.findFirst(format => Try(format.parse(str)).isSuccess)
    

    행운을 빌어 요!

  8. from https://stackoverflow.com/questions/16883591/find-the-first-element-that-satisfies-condition-x-in-a-seq by cc-by-sa and MIT license