【问题标题】:How to lexicographically compare scala tuples?如何按字典顺序比较scala元组?
【发布时间】:2012-09-06 06:03:54
【问题描述】:

给定两个相同元组的元组,我如何按字典顺序比较它们?看起来这应该像下面的 sn-p 一样简单,但事实并非如此。有什么简单的例子吗?

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

如果它们是列表,我可以定义一个递归函数来比较列表的头部,直到找到列表的差异或结尾,但我认为我不能为元组做到这一点。

【问题讨论】:

    标签: scala tuples


    【解决方案1】:

    这并不简单,因为当

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

    看起来很简单,

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

    不是。你如何处理无序类型?同一个元组位置的不同类型如何处理?

    您是否要求所有类型都相同?在这种情况下,您没有元组。元组的全部意义在于它的数量是固定的(你静态地知道它有多大)并且每个元素可以是不同的类型。

    如果我发现自己有这个问题——而且我会努力避免——我会抓住 Shapeless,将元组转换成 HList 之类的东西,然后尝试比较。

    编辑

    啊,现在容易多了:

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

    这些额外的隐式不会自动可用,因为它们在某些情况下会导致不同的隐式。

    【讨论】:

    • 好点,丹尼尔。我已经编辑了我的问题以说明比较的元组总是具有相同的数量。至于非有序类型,我的元组应该有符号(这就是我使用元组而不是列表的原因),但是因为我还没有找到任何排序符号的方式,所以我将使用数字。那我应该使用列表吗?如果我真的需要向列表中添加符号怎么办?
    • @lasaro 好吧,既然您已经简化了问题,那么有一个相对简单的解决方案。请参阅编辑后的答案。
    • 感谢 Daniel,提供了非常简洁的示例。
    • 这在 2.11.8 中似乎不起作用,但如果您按照建议 import scala.math.Ordered.orderingToOrdered stackoverflow.com/questions/19345030/… 这样做,则可以使用
    【解决方案2】:

    如果您想使用 &lt;,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: 如果您还需要比较不同大小的元组,您可以使用在所有元组类 (see documentation) 中定义的 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]。所以你必须定义一个 compare(Any,Any) 函数来处理你想要的。

      【讨论】:

      • 您可以启用-Xexperimental,它将选择LUB而不是Any
      • 感谢 Heinzi,您的完整回答。我从中学到了很多,但就我的问题水平和 scala 技能而言,Daniel 更重要。
      猜你喜欢
      • 2021-11-16
      • 2015-03-31
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多