递归打印方法中的数组

发布于 2024-12-16 13:27:56 字数 1139 浏览 1 评论 0原文

我正在尝试进行 println 替换,以更易读的格式输出嵌套集合。这最好用一个例子来说明:我想要 List(Set(Vector(1.0,1.1), Vector(0d)), Set(Vector("a", "b", "c"), Vector ("x", "y"))) 打印为

List
  Set
    Vector(1.0, 1.1)
    Vector(0.0)
  Set
    Vector(a, b, c)
    Vector(x, y)

如果没有类型擦除,这会容易得多,但我想出了

def rprint(a: Any, indent: Int = 0): Unit = a match {
  case x: Traversable[_] =>
    if (x.isEmpty)
      rprint(x.toString, indent)
    else x.head match {
      case y: Traversable[_] => {
        rprint(x.toString.takeWhile(_ != '('), indent)
        x foreach {i => rprint(i, indent + 2)}
      }
      case y => rprint(x.toString, indent)
    }
  case x => println(" " * indent + x)
}

我正在努力让它与数组很好地配合工作,而不需要大量代码重复。我希望它们能像其他集合一样工作。具体来说:

  • 数组不是可遍历

  • 可以使用genericArrayOps将数组转换为ArrayOps,即TraversableOnce,但是 TraversableOnce 没有 head 方法,所以我看不到如何获取元素检查其类型

  • toString 不像其他集合那样工作(使用.deep)

将数组合并到其中的最佳方法是什么这种方法,还是有其他更好的方法?

I'm trying to make a println replacement that outputs nested collections in a more readable format. This is best illustrated with an example: I'd like List(Set(Vector(1.0,1.1), Vector(0d)), Set(Vector("a", "b", "c"), Vector("x", "y"))) to be printed as

List
  Set
    Vector(1.0, 1.1)
    Vector(0.0)
  Set
    Vector(a, b, c)
    Vector(x, y)

This would be a lot easier without type erasure, but I've come up with

def rprint(a: Any, indent: Int = 0): Unit = a match {
  case x: Traversable[_] =>
    if (x.isEmpty)
      rprint(x.toString, indent)
    else x.head match {
      case y: Traversable[_] => {
        rprint(x.toString.takeWhile(_ != '('), indent)
        x foreach {i => rprint(i, indent + 2)}
      }
      case y => rprint(x.toString, indent)
    }
  case x => println(" " * indent + x)
}

I'm struggling with getting this to work nicely with Arrays, without substantial code duplication. I'd like them to work the same as for other collections. Specifically:

  • Arrays are not Traversable

  • could convert Arrays using genericArrayOps to ArrayOps which is TraversableOnce, but TraversableOnce doesn't have a head method, so I can't see how to get an element to check its type

  • toString doesn't work quite like other collections (use .deep)

What's the best way to incorporate Arrays into this method, or is there a different approach that would work better?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

皇甫轩 2024-12-23 13:27:56

为了使其更加灵活,我定义了一个特征,其中包括实用方法和一个抽象方法,以便可以像 Array(1,2).print 一样使用它:

trait Printable[T] {
  def str(indent: Int = 0): String
  def str: String = str(0)
  def print(indent: Int = 0): Unit = println(str(indent))
  def print: Unit = print(0)
}

然后使用隐式方法来实现它看起来 strprintAny 的方法:

implicit def any2Printable(a: Any): Printable[Any] = new Printable[Any] {
  import collection._
  def name = a match {
    case a: Array[_] => "Array"
    case g: GenTraversableLike[_, _] => g.stringPrefix 
    case i: Iterator[_] => "Iterator"
    case _ => ""
  }
  object Iter {
    def unapply(a: Any): Option[GenIterable[_]] = a match {
      case a: Array[_] => Some(a.toIterable)
      case t: GenTraversableOnce[_] => Some(t.toIterable)
      case _ => None
    }
  }
  object Nested {
    def unapply(i: GenIterable[_]): Option[GenIterable[_]] = i match {
      case nested if i.exists{case Iter(j) =>true case _ =>false} => Some(nested)
      case _ => None
    }
  }
  def str(indent: Int = 0) = " " * indent + (a match {
    case Iter(i) if i.isEmpty => name + " <empty>"
    case Iter(Nested(i)) => name + "\n" + i.map(_.str(indent+2)).mkString("\n")
    case Iter(i) => name + i.map(_.toString).mkString("(", ", ", ")")
    case _ => a.toString
  })
}

这可以处理任意深度嵌套,但不会对不支持的集合使用多行包含集合 - 例如将打印:

Array
  Set
    Array(1.0, 1.1)
    Vector <empty>
    Array <empty>
    Set
      Vector(a, b, c)
      Vector(x, y)
      List
        Set
          Array(1.0, 1.1)
          Vector(0.0)
        Set
          Vector(a, b, c)
          Vector(x, y)

To make it more flexible I'm defining a trait that includes utility methods and one abstract method, so that it can be used like that Array(1,2).print:

trait Printable[T] {
  def str(indent: Int = 0): String
  def str: String = str(0)
  def print(indent: Int = 0): Unit = println(str(indent))
  def print: Unit = print(0)
}

Then an implicit to make it look like str and print are methods of Any:

implicit def any2Printable(a: Any): Printable[Any] = new Printable[Any] {
  import collection._
  def name = a match {
    case a: Array[_] => "Array"
    case g: GenTraversableLike[_, _] => g.stringPrefix 
    case i: Iterator[_] => "Iterator"
    case _ => ""
  }
  object Iter {
    def unapply(a: Any): Option[GenIterable[_]] = a match {
      case a: Array[_] => Some(a.toIterable)
      case t: GenTraversableOnce[_] => Some(t.toIterable)
      case _ => None
    }
  }
  object Nested {
    def unapply(i: GenIterable[_]): Option[GenIterable[_]] = i match {
      case nested if i.exists{case Iter(j) =>true case _ =>false} => Some(nested)
      case _ => None
    }
  }
  def str(indent: Int = 0) = " " * indent + (a match {
    case Iter(i) if i.isEmpty => name + " <empty>"
    case Iter(Nested(i)) => name + "\n" + i.map(_.str(indent+2)).mkString("\n")
    case Iter(i) => name + i.map(_.toString).mkString("(", ", ", ")")
    case _ => a.toString
  })
}

This handles arbitrary deep nesting, but won't use multiple lines for collections that don't contain collections - for instance that would print:

Array
  Set
    Array(1.0, 1.1)
    Vector <empty>
    Array <empty>
    Set
      Vector(a, b, c)
      Vector(x, y)
      List
        Set
          Array(1.0, 1.1)
          Vector(0.0)
        Set
          Vector(a, b, c)
          Vector(x, y)
深海里的那抹蓝 2024-12-23 13:27:56

不要转换为 ArrayOps,而是尝试 WrappedArray,它有一个 head 方法并且是可遍历的。

Instead of converting to an ArrayOps, try a WrappedArray, which does have a head method and is traversable.

要走就滚别墨迹 2024-12-23 13:27:56

尝试在匹配短语中使用更通用的类型,例如 GenTraversable 来实现隐式转换。

import scala.collection.GenTraversable

def rprint[T](a: T, indent: Int = 0): Unit = a match {
  case x: String => println(" "*indent + '"' + x + '"')
  case x: GenTraversable[_] =>
    println(" "*indent + (x.stringPrefix))
    x foreach (rprint(_, indent+2))
  case x: Array[_] =>
    println(" "*indent + (a.getClass.getSimpleName))
    x foreach (rprint(_, indent+2))
  case x => println(" "*indent + x)
}

rprint(Map(1->"one",2->"two"))
rprint(List("one", "two"))           //I don't know why this type is printed weird: $colon$colon
rprint(Array(1,2))                   //This prints less weird: int[]

您可以为特定缺失类型(例如元组)添加更多案例

我无法弄清楚打印某些类型名称(例如 ListArray)有什么问题 - 请参阅示例在函数定义之后)。 a.getClass.getSimpleNamea.ToString 都返回有线字符串

我还尝试连接类型 ArrayGenTraversable - 因为存在从 Array 到 WrapperArray <: GenTraversable 的隐式转换。
下面的解决方案不满足,因为它打印“WrappedArray”而不是“Array”,
当我们使用 Array 实例调用函数时

case x: Array[_] => rprint(x:GenTraversable[_])

编辑:
我已将 GenTraversable 的名称提取器更改为 a.stringPrefix

Try tu use more general types in match phrase, like GenTraversable to witch exists implicit conversion.

import scala.collection.GenTraversable

def rprint[T](a: T, indent: Int = 0): Unit = a match {
  case x: String => println(" "*indent + '"' + x + '"')
  case x: GenTraversable[_] =>
    println(" "*indent + (x.stringPrefix))
    x foreach (rprint(_, indent+2))
  case x: Array[_] =>
    println(" "*indent + (a.getClass.getSimpleName))
    x foreach (rprint(_, indent+2))
  case x => println(" "*indent + x)
}

rprint(Map(1->"one",2->"two"))
rprint(List("one", "two"))           //I don't know why this type is printed weird: $colon$colon
rprint(Array(1,2))                   //This prints less weird: int[]

You can add more cases for specific missing types (like tuples)

I can't figure what is the problem with printing some type names (like List and Array - see the example after the function definition). Both a.getClass.getSimpleName and a.ToString returns wired string

I've also try to connect type Array and GenTraversable - because there is implicit conversion from Array to WrapperArray <: GenTraversable.
The solution below doesn't satisfy as it prints "WrappedArray" instead "Array",
when we call the function with Array instance

case x: Array[_] => rprint(x:GenTraversable[_])

Edit:
I've changed name extractor for GenTraversable to a.stringPrefix

原谅我要高飞 2024-12-23 13:27:56

我无意回答自己的问题,但我有一些有用的东西。我认为它可以改进很多,所以感谢建议。那里仍然有重复和相当丑陋的演员阵容。

  def rprint(a: Any, indent: Int = 0): Unit = {
    val (typeStr, fullStr) = a match {
      case x: collection.mutable.WrappedArray[_] => ("Array", x.deep.toString)
      case x: Traversable[_] => (x.stringPrefix, x.toString)
      case _ => ("", "")
    }
    a match {
      case x: Traversable[_] =>
        if (x.isEmpty)
          rprint(typeStr + " <empty>", indent)
        else 
          x.head match {
            case _: Array[_] => {
              rprint(typeStr, indent)
              x foreach {i => rprint(genericWrapArray(
                                       i.asInstanceOf[Array[_]]), indent + 2)}
            }
            case _: Traversable[_] => {
              rprint(typeStr, indent)
              x foreach {i => rprint(i, indent + 2)}
            }
            case _ => rprint(fullStr, indent)
          }
      case x: Array[_] => rprint(genericWrapArray(x))
      case x => println(" " * indent + x)
    }
  }

测试:

scala> rprint(List(Array(Vector(1.0,1.1), Vector(0d)), Array(Array("a", "b", "c"), Array("x", "y"))))
List
  Array
    Vector(1.0, 1.1)
    Vector(0.0)
  Array
    Array(a, b, c)
    Array(x, y)

It wasn't my intention to answer my own question, but I have something that works. I think it can be improved a lot, so suggestions are appreciated. There is still repetition and a rather ugly cast in there.

  def rprint(a: Any, indent: Int = 0): Unit = {
    val (typeStr, fullStr) = a match {
      case x: collection.mutable.WrappedArray[_] => ("Array", x.deep.toString)
      case x: Traversable[_] => (x.stringPrefix, x.toString)
      case _ => ("", "")
    }
    a match {
      case x: Traversable[_] =>
        if (x.isEmpty)
          rprint(typeStr + " <empty>", indent)
        else 
          x.head match {
            case _: Array[_] => {
              rprint(typeStr, indent)
              x foreach {i => rprint(genericWrapArray(
                                       i.asInstanceOf[Array[_]]), indent + 2)}
            }
            case _: Traversable[_] => {
              rprint(typeStr, indent)
              x foreach {i => rprint(i, indent + 2)}
            }
            case _ => rprint(fullStr, indent)
          }
      case x: Array[_] => rprint(genericWrapArray(x))
      case x => println(" " * indent + x)
    }
  }

test:

scala> rprint(List(Array(Vector(1.0,1.1), Vector(0d)), Array(Array("a", "b", "c"), Array("x", "y"))))
List
  Array
    Vector(1.0, 1.1)
    Vector(0.0)
  Array
    Array(a, b, c)
    Array(x, y)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文