【问题标题】:Deep-reverse of nested lists in ScalaScala中嵌套列表的深度反转
【发布时间】:2012-09-20 19:55:16
【问题描述】:

我想在 Scala 中递归地反转列表列表。

我在 Python 中写过这样的深度列表反转:

def deepReverse(items):
    if type(items) == list:
        return [deepReverse(item) for item in reversed(items)]
    else: 
        return items

我将如何在 Scala 中做同样的事情?问题不在于算法——而是类型的东西,我比较新。

我需要该函数将 [T] 列表或 List[List[T]] 或 T 列表和 T 列表获取到任意深度。我尝试根据我在其他地方看到的示例制作一个案例类来做到这一点。我不想要一个只返回 Any 并接受 Any 的函数;感觉像是在作弊。

case class NL[+T](val v : Either[List[NL[T]],T])

不过,我还是无法完全平衡我的类型。我是 Scala 的新手,但我认为这将是一个处理递归和打字的绝佳机会。

【问题讨论】:

    标签: list scala recursion


    【解决方案1】:

    编写 sschaef 提出的适用于任意嵌套列表的类型类方法的版本实际上并不难:

    trait Reverser[C] {
      def reverse(xs: C): C
    }
    
    implicit def rev[A](implicit ev: Reverser[A] = null) = new Reverser[List[A]] {
      def reverse(xs: List[A]) =
        Option(ev).map(r => xs map r.reverse).getOrElse(xs).reverse
    }
    
    def deepReverse[A](xs: A)(implicit ev: Reverser[A]): A = ev.reverse(xs)
    

    rev 方法中的隐含参数ev 证明A 本身是可逆的,如果ev 为空,则表示不可逆。如果我们有证据证明A 是可逆的,我们使用它来反转List[A] 的元素(这就是map 正在做的事情),然后我们反转列表本身。如果我们没有这个证据(getOrElse 案例),我们可以颠倒列表。

    我们可以这样写rev 不那么简洁(但可能更高效):

    implicit def rev[A](implicit ev: Reverser[A] = null) = if (ev == null) {
      new Reverser[List[A]] {
        def reverse(xs: List[A]) = xs.reverse
      }
    } else {
      new Reverser[List[A]] {
       def reverse(xs: List[A]) = (xs map ev.reverse).reverse
      }
    }
    

    要测试这两个版本中的任何一个,我们可以编写以下代码:

    scala> deepReverse(List.tabulate(3)(identity))
    res0: List[Int] = List(2, 1, 0)
    
    scala> deepReverse(List.tabulate(2,3) { case (a, b) => a + b })
    res1: List[List[Int]] = List(List(3, 2, 1), List(2, 1, 0))
    
    scala> deepReverse(List.tabulate(2, 3, 4, 5, 6) {
         |   case (a, b, c, d, e) => a + b + c + d + e
         | }).head.head.head.head
    res2: List[Int] = List(15, 14, 13, 12, 11, 10)
    

    正如预期的那样。


    我应该补充一点,在这种情况下,以下是一个更常见的习语:

    trait ReverserLow {
      implicit def listReverser[A] = new Reverser[List[A]] {
        def reverse(xs: List[A]) = xs.reverse
      }
    }
    
    object ReverserHigh extends ReverserLow {
      implicit def nestedListReverser[A](implicit ev: Reverser[A]) =
        new Reverser[List[A]] {
          def reverse(xs: List[A]) = xs.map(ev.reverse).reverse
        }
    }
    
    import ReverserHigh._
    

    如果我们只是将listReversernestedListReverser 写在同一级别,当我们尝试反转列表列表时会出现以下错误:

    scala> deepReverse(List.tabulate(2, 3)(_ + _))
    <console>:12: error: ambiguous implicit values:
     both method listReverser...
     and method nestedListReverser...
     match expected type Reverser[List[List[Int]]]
                  deepReverse(List.tabulate(2, 3)(_ + _))
    

    对两者进行优先级排序的标准方法是将较低优先级隐含在特征 (WhateverLow) 中,将另一个隐含在扩展该特征的对象 (WhateverHigh) 中。但是,在这样一个相当简单的情况下,在上面的rev 方法中使用默认参数技巧会更简洁(在我看来也更清晰)。但您更有可能在其他人的代码中看到其他版本。

    【讨论】:

    • 该死!我没想过将类型类注入自身。绝招!
    • 太棒了!那么,如果没有返回地图,getOrElse 会返回值本身吗?非常感谢。
    • @GrittyKitty:谢谢!在这里查看我的更新以获取更多关于这个技巧的解释。
    【解决方案2】:

    如果你想要这个真正的类型安全,那么类型类模式就是你的朋友:

    object Reverse extends App {
    
      trait Reverser[C] {
        def reverse(xs: C): C
      }
    
      implicit def List1Reverser[A] = new Reverser[List[A]] {
        def reverse(xs: List[A]) =
          xs.reverse
      }
    
      implicit def List2Reverser[A] = new Reverser[List[List[A]]] {
        def reverse(xs: List[List[A]]) =
          xs.map(_.reverse).reverse
      }
    
      implicit def List3Reverser[A] = new Reverser[List[List[List[A]]]] {
        def reverse(xs: List[List[List[A]]]) =
          xs.map(_.map(_.reverse).reverse).reverse
      }
    
      def deepReverse[A](xs: A)(implicit rev: Reverser[A]): A =
        rev.reverse(xs)
    
      val xs = List(1,2)
      val xxs = List(List(1,2),List(1,2),List(1,2))
      val xxxs = List(List(List(1,2),List(1,2)),List(List(1,2),List(1,2)),List(List(1,2),List(1,2)))
    
      println(deepReverse(xs))
      println(deepReverse(xxs))
      println(deepReverse(xxxs))
    }
    

    唯一的问题是您需要为每个嵌套列表类型提供一个类型类。

    【讨论】:

    • 非常感谢,虽然我肯定在寻找任意深度的嵌套,这意味着我真的不能为每个级别定义一个新的反向器。
    • 你确定你会有“真的”任意深度吗?我的意思是10级(例如)还不够吗?
    • +1 但任意深度都是可能的(如果合适的话,我会在此处的评论中发布我的答案)。
    • @sschaef:这是一个好奇的问题,完全脱离了实用性。非常感谢:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-04
    • 1970-01-01
    • 2021-01-28
    • 2021-10-20
    • 1970-01-01
    相关资源
    最近更新 更多