Как лексикографически сравнивать кортежи scala?

Учитывая два кортежа одной и той же ясности, как я могу их сравнить с лексикографией? Кажется, что это должно быть так же просто, как в следующем фрагменте, но это не так. Любой простой пример того, как это сделать?

var x = (1,2,3) < (1,2,4)

Были ли они списки, я мог бы определить рекурсивную функцию, которая бы сравнивала голову списков, пока не была найдена разница или конец списка, но я не думаю, что могу сделать это для кортежей.

Ответ 1

Это не просто, потому что пока

var x = (1,2,3) < (1,2)

выглядит довольно просто,

var x = (1,false,3) < (1,2)

нет. Как вы относитесь к не упорядоченным типам? Как вы относитесь к разным типам в одной и той же позиции кортежа?

Предоставляете ли вы, чтобы все типы были одинаковыми? В этом случае у вас нет кортежа. Вся суть кортежа состоит в том, что его арность фиксирована (вы статически знаете, насколько она велика), и каждый элемент может быть другого типа.

Если бы я столкнулся с этой проблемой - и я бы очень старался не делать этого - я бы захватил Shapeless, превратил кортежи в нечто вроде HLists, а затем попытаюсь сравнить это.

ИЗМЕНИТЬ

А, теперь это намного проще:

import scala.math.Ordering.Implicits._
var x = (1,2,3) < (1,2,4)

Эти дополнительные импликации не доступны автоматически, поскольку в некоторых случаях они могут привести к расплывчатым имплицитам.

Ответ 2

Решение Daniel работает, если вы хотите использовать <, но если вам нужен метод compare, вы можете сделать следующее (например).

implicitly[Ordering[Tuple2[Int, Int]]].compare((1,2), (2,3))

Определены порядки для всех кортежей со сравнимыми частями.

Ответ 3

Самый простой способ - определить неявный Ordering [T] на них, но тогда вы должны передать это упорядочение функции сортировки (или любую другую функцию, которая хочет их сравнить). Это также можно передать неявно.

Другим способом было бы: расширить класс кортежа на < оператор через неявный листинг:

implicit def compareTuple[T](lhs: (T,T)) = new {
   def <(rhs: (T,T)) = lhs._1<rhs._1 || (lhs._1==rhs._1 && lhs._2<rhs._2)
}

изменить Если вы хотите иметь и другие операторы сравнения, вы можете получить их, наследуя от Ordered [T]:

implicit def compareTuple[T](lhs: (T,T)) = new Ordered[(T,T)] {
   def compare(rhs: (T,T)) = ...
}

edit2: Если вам также нужно сравнить кортежи разных размеров, вы можете использовать функцию productIterator, которая определена во всех классах кортежей (см. Документацию) и позволяет вам чтобы получить итератор над кортежем. Таким образом, вы можете написать такую ​​функцию, как если бы вы сделали это со списком.

Edit3: Это будет примерно так:

implicit def compareTuple[T <: Product](lhs: T) = new Ordered[T] {
    def compare[U <: Product](rhs: U) = {
        def compare(lhs: Any, rhs: Any) = ...
        def iteratorCompare(lhs: Iterator[Any], rhs: Iterator[Any]):Int = 
            if(!lhs.hasNext)
                if(!rhs.hasNext)
                    0
                else
                    -1
            else if(!rhs.hasNext)
                1
            else
                compare(lhs.next,rhs.next)
        iteratorCompare(lhs.productIterator,rhs.productIterator)
    }
}

Но при таком подходе вам нужно позаботиться о типах. Поскольку функция не знает типы элементов кортежа (они могут быть разными внутри одного и того же кортежа), он может предоставить только Iterator [Any]. Поэтому вам нужно определить функцию сравнения (Any, Any) для обработки того, что вы хотите.