[scala] 간단한 케이스 클래스에 대한 Ordering을 정의하는 쉬운 관용적 방법

간단한 스칼라 케이스 클래스 인스턴스 목록이 있고를 사용하여 예측 가능한 사전 순으로 인쇄하려고 list.sorted하지만 “…에 대해 정의 된 암시 적 순서 없음”을 수신합니다.

케이스 클래스에 대한 사전 순서를 제공하는 암시적인 것이 있습니까?

사전 식 순서를 케이스 클래스에 혼합하는 간단한 관용적 방법이 있습니까?

scala> case class A(tag:String, load:Int)
scala> val l = List(A("words",50),A("article",2),A("lines",7))

scala> l.sorted.foreach(println)
<console>:11: error: No implicit Ordering defined for A.
          l.sorted.foreach(println)
            ^

나는 ‘해킹’에 만족하지 않습니다.

scala> l.map(_.toString).sorted.foreach(println)
A(article,2)
A(lines,7)
A(words,50)



답변

개인적으로 가장 좋아하는 방법은 명확하고 간결하며 정확하기 때문에 Tuples에 대해 제공된 암시 적 순서를 사용하는 것입니다.

case class A(tag: String, load: Int) extends Ordered[A] {
  // Required as of Scala 2.11 for reasons unknown - the companion to Ordered
  // should already be in implicit scope
  import scala.math.Ordered.orderingToOrdered

  def compare(that: A): Int = (this.tag, this.load) compare (that.tag, that.load)
}

때문 작동 동반자Ordered 암시 적 변환의 정의 Ordering[T]Ordered[T]있는이 구현하는 모든 클래스에 대한 범위에 Ordered. Orderings에 대한 암시 적 s 의 존재는 튜플의 모든 요소 에 대해 암시 적 존재 를 제공하는 Tuple에서 TupleN[...]로 변환 할 수있게합니다 .Ordered[TupleN[...]]Ordering[TN]T1, ..., TNOrdering

튜플에 대한 암시 적 순서는 복합 정렬 키와 관련된 모든 정렬 시나리오를위한 것입니다.

as.sortBy(a => (a.tag, a.load))

이 답변이 인기있는 것으로 입증 되었기 때문에 다음과 같은 솔루션이 어떤 상황에서는 엔터프라이즈 급 ™으로 간주 될 수 있다는 점에 주목하여 확장하고 싶습니다.

case class Employee(id: Int, firstName: String, lastName: String)

object Employee {
  // Note that because `Ordering[A]` is not contravariant, the declaration
  // must be type-parametrized in the event that you want the implicit
  // ordering to apply to subclasses of `Employee`.
  implicit def orderingByName[A <: Employee]: Ordering[A] =
    Ordering.by(e => (e.lastName, e.firstName))

  val orderingById: Ordering[Employee] = Ordering.by(e => e.id)
}

주어진 es: SeqLike[Employee]경우 es.sorted()이름 es.sorted(Employee.orderingById)별로 정렬하고 ID별로 정렬합니다. 다음과 같은 몇 가지 이점이 있습니다.

  • 정렬은 단일 위치에서 표시되는 코드 아티팩트로 정의됩니다. 여러 필드에 대해 복잡한 정렬이있는 경우 유용합니다.
  • 스칼라 라이브러리에 구현 된 대부분의 정렬 기능은의 인스턴스를 사용하여 작동 Ordering하므로 정렬을 제공하면 대부분의 경우 암시 적 변환이 직접 제거됩니다.

답변

object A {
  implicit val ord = Ordering.by(unapply)
}

A가 변경 될 때마다 자동으로 업데이트된다는 이점이 있습니다. 그러나 A의 필드는 주문시 사용할 순서대로 배치되어야합니다.


답변

요약하면 다음과 같은 세 가지 방법이 있습니다.

  1. 일회성 정렬의 경우 @Shadowlands가 보여준 것처럼 .sortBy 메서드를 사용하십시오.
  2. @Keith가 말했듯이 정렬을 재사용하기 위해 Ordered 특성으로 케이스 클래스를 확장하십시오.
  3. 맞춤 주문을 정의합니다. 이 솔루션의 이점은 순서를 재사용 할 수 있고 동일한 클래스의 인스턴스를 정렬하는 여러 가지 방법이 있다는 것입니다.

    case class A(tag:String, load:Int)
    
    object A {
      val lexicographicalOrdering = Ordering.by { foo: A =>
        foo.tag
      }
    
      val loadOrdering = Ordering.by { foo: A =>
        foo.load
      }
    }
    
    implicit val ord = A.lexicographicalOrdering
    val l = List(A("words",1), A("article",2), A("lines",3)).sorted
    // List(A(article,2), A(lines,3), A(words,1))
    
    // now in some other scope
    implicit val ord = A.loadOrdering
    val l = List(A("words",1), A("article",2), A("lines",3)).sorted
    // List(A(words,1), A(article,2), A(lines,3))

질문에 답하기 List ((2,1), (1,2))와 같은 마법을 할 수있는 Scala에 포함 된 표준 함수가 있습니까?

예를 들어 문자열, 최대 9 개의 튜플 등 의 사전 정의 된 순서 가 있습니다.

필드 이름이 사전에 알려지지 않았고 (적어도 매크로 마법없이) 케이스 클래스 필드에 액세스 할 수 없기 때문에 롤오프하기가 쉽지 않기 때문에 케이스 클래스에는 그런 것이 없습니다. 이름 / 제품 반복자 사용.


답변

unapply컴패니언 객체 의 메서드는 케이스 클래스에서로 변환을 제공합니다 Option[Tuple]. 여기서는 케이스 클래스 Tuple의 첫 번째 인수 목록에 해당하는 튜플입니다. 다시 말해:

case class Person(name : String, age : Int, email : String)

def sortPeople(people : List[Person]) =
    people.sortBy(Person.unapply)


답변

sortBy 메서드는이를 수행하는 일반적인 방법 중 하나입니다. 예 : ( tag필드에 정렬 ) :

scala> l.sortBy(_.tag)foreach(println)
A(article,2)
A(lines,7)
A(words,50)


답변

케이스 클래스 를 사용했기 때문에 다음 과 같이 Ordered로 확장 할 수 있습니다 .

case class A(tag:String, load:Int) extends Ordered[A] {
  def compare( a:A ) = tag.compareTo(a.tag)
}

val ls = List( A("words",50), A("article",2), A("lines",7) )

ls.sorted


답변