Массивы в методе рекурсивной печати - PullRequest
2 голосов
/ 16 ноября 2011

Я пытаюсь сделать замену 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)
}

Я изо всех сил пытаюсь заставить это работать хорошо с Массивами, без существенного дублирования кода.Я бы хотел, чтобы они работали так же, как и для других коллекций.В частности:

  • Массивы не Traversable

  • могут преобразовывать массивы с использованием genericArrayOps в ArrayOps, что составляет TraversableOnce, но TraversableOnce не имеет head метода, поэтому я не вижу, как заставить элемент проверять его тип

  • toString не работает как другие коллекции (используйте .deep)

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

Ответы [ 4 ]

1 голос
/ 17 ноября 2011

Чтобы сделать его более гибким, я определяю черту, которая включает в себя служебные методы и один абстрактный метод, чтобы его можно было использовать вот так 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)
}

Затем подразумевается сделать этоstr и print представляют собой методы 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
  })
}

Это обрабатывает произвольное глубокое вложение, но не будет использовать несколько строк для коллекций, которые не содержат коллекций - например, чтобудет печатать:

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)
1 голос
/ 16 ноября 2011

Попробуйте использовать более общие типы в фразе совпадения, например 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[]

Вы можете добавить больше случаев для определенных пропущенных типов (например, кортежей)

Я не могу понять, в чем проблема с печатью имен некоторых типов (например, List и Array - см. Пример после определения функции). И a.getClass.getSimpleName, и a.ToString возвращают проводную строку

Я также пытался соединить типы Array и GenTraversable - потому что существует неявное преобразование из Array в WrapperArray <: GenTraversable.
Приведенное ниже решение не удовлетворяет, так как печатает «WrappedArray» вместо «Array», когда мы вызываем функцию с Array instance

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

Редактировать:
Я изменил имя экстрактора для GenTraversable на a.stringPrefix

1 голос
/ 16 ноября 2011

Вместо преобразования в ArrayOps попробуйте WrappedArray , у которого есть метод head и который можно пройти.

0 голосов
/ 16 ноября 2011

Я не собирался отвечать на свой вопрос, но у меня есть кое-что, что работает.Я думаю, что это можно улучшить, поэтому предложения приветствуются.Там все еще есть повторение и довольно уродливый состав.

  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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...