scala pretty-print case-class

Scala: cómo imprimir clases de casos como árbol(bastante impreso)



pretty-print case-class (6)

Al igual que los combinadores de analizadores, Scala ya contiene lindos combinadores de impresoras en la biblioteca estándar. No está diciendo claramente en su pregunta si necesita la solución que hace "reflexión" o si le gustaría construir la impresora de forma explícita. (aunque su "pregunta de bonificación" insinúa que probablemente quiera una solución "reflexiva")

De todos modos, en el caso de que desee desarrollar una impresora bonita simple utilizando una biblioteca Scala normal, aquí está. El siguiente código es 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) ))

Pregunta adicional: envuelva las impresoras en clases de tipos para una mayor capacidad de compilación.

Estoy haciendo un analizador sintáctico con Scala Combinators. Es asombroso. Con lo que termino es una larga lista de clases de casos encriptados, como: ClassDecl(Complex,List(VarDecl(Real,float), VarDecl(Imag,float))) , solo 100 veces más. Me preguntaba si existe una buena manera de imprimir clases de casos como estos de forma similar a un árbol para que sea más fácil de leer ... (o alguna otra forma de Pretty Print )

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

^ Quiero terminar con algo como esto

editar: pregunta de bonificación

¿Hay también una manera de mostrar el nombre del parámetro ...? Me gusta: ClassDecl(name=Complex, fields=List( ... ) ?


Echa un vistazo a una pequeña biblioteca de extensiones llamada sext . Exporta estas dos funciones exactamente para fines como ese.

Así es como se puede usar para su ejemplo:

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) }

A continuación se muestra el resultado de este programa:

treeString output: ClassDecl: - Complex - List: | - VarDecl: | | - 1 | | - abcd | - VarDecl: | | - 2 | | - efgh valueTreeString output: - kind: - list: | - - a: | | | 1 | | - b: | | | abcd | - - a: | | | 2 | | - b: | | | efgh


La mejor y más concisa experiencia de caja que he encontrado es con la biblioteca de impresión Kiama . No imprime nombres de miembros sin utilizar combinadores adicionales, pero solo import org.kiama.output.PrettyPrinter._; pretty(any(data)) import org.kiama.output.PrettyPrinter._; pretty(any(data)) tiene un gran comienzo:

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)

Produce:

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

Tenga en cuenta que este es solo el ejemplo más básico. Kiama PrettyPrinter es una biblioteca extremadamente poderosa con un amplio conjunto de combinadores específicamente diseñados para espacios inteligentes, líneas, anidamiento y agrupamiento. Es muy fácil de ajustar para satisfacer sus necesidades. A partir de esta publicación, está disponible en SBT con:

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


Usando la reflexión

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 } }


Use la biblioteca com.lihaoyi.pprint.

libraryDependencies += "com.lihaoyi" %% "pprint" % "0.4.1" val data = ... val str = pprint.tokenize(data).mkString println(str)

también puede configurar ancho, alto, sangría y colores:

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

Documentos: http://www.lihaoyi.com/PPrint/


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!") } }