斯卡拉 - 如何打印的情况下类,如(漂亮印刷)树(Scala - how to print case

2019-08-18 07:51发布

我正在使用Scala组合程序解析器。 太棒了。 我结束了是entagled case类的一个长长的清单,如: ClassDecl(Complex,List(VarDecl(Real,float), VarDecl(Imag,float)))只是100倍更长的时间。 我在想,如果有打印case类像这些在树状方式,使其更易于阅读的好办法..? (或漂亮打印的一些其它形式的)

ClassDecl
  name = Complex
  fields =
  - VarDecl
      name = Real
      type = float
  - VarDecl
      name = Imag
      type = float

^我想这样的事情结束了

编辑:奖金问题

有没有也是一种方式显示参数的名称..? 像: ClassDecl(name=Complex, fields=List( ... )

Answer 1:

检查出一个名为小扩展库六重峰 。 它导出了这两个功能完全相同的目的,这样的。

下面是它如何使用您的例子:

object Demo extends App {

  import sext._

  case class ClassDecl( kind : Kind, list : List[ VarDecl ] )
  sealed trait Kind
  case object Complex extends Kind
  case class VarDecl( a : Int, b : String )


  val data = ClassDecl(Complex,List(VarDecl(1, "abcd"), VarDecl(2, "efgh")))
  println("treeString output:\n")
  println(data.treeString)
  println()
  println("valueTreeString output:\n")
  println(data.valueTreeString)

}

以下是该程序的输出:

treeString output:

ClassDecl:
- Complex
- List:
| - VarDecl:
| | - 1
| | - abcd
| - VarDecl:
| | - 2
| | - efgh

valueTreeString output:

- kind:
- list:
| - - a:
| | | 1
| | - b:
| | | abcd
| - - a:
| | | 2
| | - b:
| | | efgh


Answer 2:

使用com.lihaoyi.pprint库。

libraryDependencies += "com.lihaoyi" %% "pprint" % "0.4.1"

val data = ...

val str = pprint.tokenize(data).mkString
println(str)

您还可以配置宽度,高度,缩进及颜色:

pprint.tokenize(data, width = 80).mkString

文档: http://www.lihaoyi.com/PPrint/



Answer 3:

就像解析器组合,斯卡拉已经包含在标准库漂亮的打印组合程序。 你是不是在你的问题说清楚,如果你需要,做“反思”的解决方案,或者你想明确地建立打印机。 (虽然你的“奖金问题”暗示你可能要“反思”的解决方案)

总之,在情况下,你想使用普通Scala库开发简单漂亮的打印机,在这儿呢。 下面的代码是REPLable。

case class VarDecl(name: String, `type`: String)
case class ClassDecl(name: String, fields: List[VarDecl])

import scala.text._
import Document._

def varDoc(x: VarDecl) =
  nest(4, text("- VarDecl") :/:
    group("name = " :: text(x.name)) :/:
    group("type = " :: text(x.`type`))
  )

def classDoc(x: ClassDecl) = {
  val docs = ((empty:Document) /: x.fields) { (d, f) => varDoc(f) :/: d }
  nest(2, text("ClassDecl") :/:
    group("name = " :: text(x.name)) :/:
    group("fields =" :/: docs))
}

def prettyPrint(d: Document) = {
  val writer = new java.io.StringWriter
  d.format(1, writer)
  writer.toString
}

prettyPrint(classDoc(
  ClassDecl("Complex", VarDecl("Real","float") :: VarDecl("Imag","float") :: Nil)
))

奖金的问题 :包装打印机进型类更大的可组合性。



Answer 4:

import java.lang.reflect.Field
...

/**
  * Pretty prints case classes with field names.
  * Handles sequences and arrays of such values.
  * Ideally, one could take the output and paste it into source code and have it compile.
  */
def prettyPrint(a: Any): String = {
  // Recursively get all the fields; this will grab vals declared in parents of case classes.
  def getFields(cls: Class[_]): List[Field] =
    Option(cls.getSuperclass).map(getFields).getOrElse(Nil) ++
        cls.getDeclaredFields.toList.filterNot(f =>
          f.isSynthetic || java.lang.reflect.Modifier.isStatic(f.getModifiers))
  a match {
    // Make Strings look similar to their literal form.
    case s: String =>
      '"' + Seq("\n" -> "\\n", "\r" -> "\\r", "\t" -> "\\t", "\"" -> "\\\"", "\\" -> "\\\\").foldLeft(s) {
        case (acc, (c, r)) => acc.replace(c, r) } + '"'
    case xs: Seq[_] =>
      xs.map(prettyPrint).toString
    case xs: Array[_] =>
      s"Array(${xs.map(prettyPrint) mkString ", "})"
    // This covers case classes.
    case p: Product =>
      s"${p.productPrefix}(${
        (getFields(p.getClass) map { f =>
          f setAccessible true
          s"${f.getName} = ${prettyPrint(f.get(p))}"
        }) mkString ", "
      })"
    // General objects and primitives end up here.
    case q =>
      Option(q).map(_.toString).getOrElse("¡null!")
  }
}


Answer 5:

最好的,最简洁的“乱”框的经验,我发现与凯马漂亮印刷库 。 它并不不必使用其他组合程序打印成员的名字,但只有import org.kiama.output.PrettyPrinter._; pretty(any(data)) import org.kiama.output.PrettyPrinter._; pretty(any(data))你有一个很好的开始:

case class ClassDecl( kind : Kind, list : List[ VarDecl ] )
sealed trait Kind
case object Complex extends Kind
case class VarDecl( a : Int, b : String )

val data = ClassDecl(Complex,List(VarDecl(1, "abcd"), VarDecl(2, "efgh")))
import org.kiama.output.PrettyPrinter._

// `w` is the wrapping width. `1` forces wrapping all components.
pretty(any(data), w=1)

生产:

ClassDecl (
    Complex (),
    List (
        VarDecl (
            1,
            "abcd"),
        VarDecl (
            2,
            "efgh")))

请注意,这只是最基本的例子。 凯马PrettyPrinter是一组丰富的专门针对智能间距,换行,嵌套和分组组合程序设计的一个非常强大的库。 这是很容易调整,以满足您的需求。 由于此张贴,它可以在SBT有:

libraryDependencies += "com.googlecode.kiama" %% "kiama" % "1.8.0"


Answer 6:

使用反射

import scala.reflect.ClassTag
import scala.reflect.runtime.universe._

object CaseClassBeautifier  {
  def getCaseAccessors[T: TypeTag] = typeOf[T].members.collect {
    case m: MethodSymbol if m.isCaseAccessor => m
  }.toList

  def nice[T:TypeTag](x: T)(implicit classTag: ClassTag[T]) : String = {
    val instance = x.asInstanceOf[T]
    val mirror = runtimeMirror(instance.getClass.getClassLoader)
    val accessors = getCaseAccessors[T]
    var res = List.empty[String]
    accessors.foreach { z ⇒
      val instanceMirror = mirror.reflect(instance)
      val fieldMirror = instanceMirror.reflectField(z.asTerm)
      val s = s"${z.name} = ${fieldMirror.get}"
      res = s :: res
    }
    val beautified = x.getClass.getSimpleName + "(" + res.mkString(", ") + ")"
    beautified
  }
}


Answer 7:

开始Scala 2.13case class ES(这是一个实现Product )现在提供了productElementNames方法,它返回了自己的字段名称的迭代器。

联合Product::productIterator它提供了一个案例类的价值观,我们有一个简单的方法来打印出更漂亮case类,而不需要反思

def pprint(obj: Any, depth: Int = 0, paramName: Option[String] = None): Unit = {

  val indent = "  " * depth
  val prettyName = paramName.fold("")(x => s"$x: ")
  val ptype = obj match { case _: Iterable[Any] => "" case obj: Product => obj.productPrefix case _ => obj.toString }

  println(s"$indent$prettyName$ptype")

  obj match {
    case seq: Iterable[Any] =>
      seq.foreach(pprint(_, depth + 1))
    case obj: Product =>
      (obj.productIterator zip obj.productElementNames)
        .foreach { case (subObj, paramName) => pprint(subObj, depth + 1, Some(paramName)) }
    case _ =>
  }
}

这对于您的具体方案:

// sealed trait Kind
// case object Complex extends Kind
// case class VarDecl(a: Int, b: String)
// case class ClassDecl(kind: Kind, decls: List[VarDecl])

val data = ClassDecl(Complex, List(VarDecl(1, "abcd"), VarDecl(2, "efgh")))

pprint(data)

生产:

ClassDecl
  kind: Complex
  decls: 
    VarDecl
      a: 1
      b: abcd
    VarDecl
      a: 2
      b: efgh


Answer 8:

这是@F的shamless复制粘贴。 P自由,但

  • 我已经添加了一个缺口功能
  • 稍作修改,使输出将是正确的Scala的风格(将编译所有的灵长类动物的类型)
  • 固定字符串文字错误
  • 增加了支持java.sql.Timestamp (因为我用这个火花了很多)

然后!

import java.lang.reflect.Field
import java.sql.Timestamp

  def prettyPrint(a: Any, indentSize: Int = 0): String = {
    // Recursively get all the fields; this will grab vals declared in parents of case classes.
    def getFields(cls: Class[_]): List[Field] =
      Option(cls.getSuperclass).map(getFields).getOrElse(Nil) ++
        cls.getDeclaredFields.toList.filterNot(f =>
          f.isSynthetic || java.lang.reflect.Modifier.isStatic(f.getModifiers))

    val indent = List.fill(indentSize)(" ").mkString

    val newIndentSize = indentSize + 2
    (a match {
      // Make Strings look similar to their literal form.
      case string: String =>
        val conversionMap = Map('\n' -> "\\n", '\r' -> "\\r", '\t' -> "\\t", '\"' -> "\\\"", '\\' -> "\\\\")
        string.map(c => conversionMap.getOrElse(c, c)).mkString("\"", "", "\"")
      case xs: Seq[_] =>
        xs.map(prettyPrint(_, newIndentSize)).toString
      case xs: Array[_] =>
        s"Array(${xs.map(prettyPrint(_, newIndentSize)).mkString(", ")})"
      case map: Map[_, _] =>
        s"Map(\n" + map.map {
          case (key, value) => "  " + prettyPrint(key, newIndentSize) + " -> " + prettyPrint(value, newIndentSize)
        }.mkString(",\n") + "\n)"
      case None => "None"
      case Some(x) => "Some(" + prettyPrint(x, newIndentSize) + ")"
      case timestamp: Timestamp => "new Timestamp(" + timestamp.getTime + "L)"
      case p: Product =>
        s"${p.productPrefix}(\n${
          getFields(p.getClass).map { f =>
            f.setAccessible(true)
            s"  ${f.getName} = ${prettyPrint(f.get(p), newIndentSize)}"
          }
          .mkString(",\n")
        }\n)"
      // General objects and primitives end up here.
      case q =>
        Option(q).map(_.toString).getOrElse("null")
    })
      .split("\n", -1).mkString("\n" + indent)
  }

case class Foo(bar: String, bob: Int)

case class Alice(foo: Foo, opt: Option[String], opt2: Option[String])

scala> prettyPrint(Alice(Foo("hello world", 10), Some("asdf"), None))
res6: String =
Alice(
  foo = Foo(
    bar = "hello world",
    bob = 10
  ),
  opt = Some("asdf"),
  opt2 = None
)


文章来源: Scala - how to print case classes like (pretty printed) tree