3
votes

Comment paramétrer les chaînes de format utilisées par StringContext?

Disons que j'ai une ligne de code comme celle-ci:

StringContext(...).f(...)

Ce que je veux, c'est faire le 20 (la largeur du nom ) quelque chose qui lui-même peut être paramétré. Comment puis-je faire cela?

Et si je voulais également paramétrer la largeur de 7.2 pour amt . Est-ce possible?

[Edit: J'espère mettre des choses comme 20 et 7.2 dans des variables afin qu'elles ne soient pas codées en dur dans la chaîne de format.]

J'ai essayé d'utiliser directement StringContext , mais il semble que je ne comprends pas quelle que soit l'extension de macro qui se passe dans:

log.info(f"$name%-20s $amt%7.2f")


0 commentaires

3 Réponses :


1
votes

Vous n'êtes pas loin de la vérité:

val len = 20
val prec = 7.2
val template = s"$$name%${len}s $$amt%${prec}f"

donne:

| James 1234,12 |

En ce qui concerne le passage de 20 et 7.2 en paramètre: le modèle peut être généré comme:

val amt= 1234.1234d
val name = "James"
println(f"|$name%20s $amt%7.2f|")

ce qui donne: $ name% 20 $ amt% 7.2f

Mais l'interpolation de chaîne ne semble pas être la solution car même désugée dans StringContext, elle se fait au moment de la compilation, puis s "$ template renvoie $ name% 20s $ amt% 7.2f .

Je pense que cela fonctionnait avec une autre langue


2 commentaires

Merci, mais j'espère trouver un moyen de mettre 20 dans une variable afin que 20 ne soit pas codé en dur dans la chaîne de format.


réponse juste éditée: l'interpolation ne semble pas être la solution



1
votes

vous pouvez implémenter le vôtre StringContext
remarque: s "a = $ {a}, b = $ {b}" comme StringContext ("a =", ", b ="). s (a, b)

val amt = 1234.1234d
val name = "James"
val len = Format("20s")
val prec = Format("7.2f")

myFormat"$name%$len,$amt%$prec," //"               James,1234.12,"

// test

case class Format(t: String)

// this implement is not type safe in Compile time,
implicit class MyFormat(val sc: StringContext) {

  def myFormat(args: Any*) = {
    val partIter = sc.parts.iterator
    var stringFormatArgs = List.empty[Object]
    val sb = new StringBuilder(partIter.next())

    def impl(list: List[Any]): Unit = {
      list match {
        case (_: Format) :: tail                  =>
          throw new Exception("")
        case (any: Object) :: (format: Format) :: tail =>
          sb.append(partIter.next()).append(format.t).append(partIter.next())
          stringFormatArgs = any :: stringFormatArgs
          impl(tail)
        case (any: Any) :: tail                        =>
          sb.append(any.toString).append(partIter.next())
          impl(tail)
        case Nil                                       =>
      }
    }

    impl(args.toList)
    String.format(sb.toString(), stringFormatArgs.reverse: _*)
  }
}


0 commentaires

1
votes

Vous pouvez le faire en deux parties.

s"%${nLen}s%${ff}f".format(name, amt)
//res1: String = Jan       1.100

Ou en un seul passage.

val name = "Jan"
val nLen = -10  //name length

val amt = 1.1
val ff = 5.3  //float format

s"%${nLen}s".format(name) + s"%${ff}f".format(amt)
//res0: String = Jan       1.100


0 commentaires